Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ammario/weakmap
Go map that automatically evicts based on memory pressure
https://github.com/ammario/weakmap
Last synced: 25 days ago
JSON representation
Go map that automatically evicts based on memory pressure
- Host: GitHub
- URL: https://github.com/ammario/weakmap
- Owner: ammario
- License: cc0-1.0
- Created: 2024-03-17T16:49:08.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2024-04-02T19:31:08.000Z (7 months ago)
- Last Synced: 2024-06-21T14:09:15.651Z (5 months ago)
- Language: Go
- Homepage:
- Size: 22.5 KB
- Stars: 9
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# weakmap
[![Go Reference](https://pkg.go.dev/badge/github.com/ammario/weakmap.svg)](https://pkg.go.dev/github.com/ammario/weakmap@main)Package `weakmap` implements a weak map for Go without `unsafe` or pointer magic.
Instead, it uses finalizers to hook into garbage collection cycles and evicts
old entries based on a combination of a least-recently-used (LRU) policy
and memory pressure reported by the runtime.The primary use case for this type of weak map is memory-caching. Compared to a
traditional in-memory caches, you don't have to think about deadlines or background cleanup tasks.
You can carelessly throw stuff into the weak map and let the GC take care of the rest.Install:
```
go get github.com/ammario/weakmap@main
```> [!WARNING]
> `weakmap` relies on `runtime.SetFinalizer` and, specifically, that
> objects larger than 16 bytes will not be batched into shared allocation
> slots. While this behavior is documented, whether the Go Authors consider
> it apart of the compatibility promise is dubious at best.See [ammario/tlru](https://github.com/ammario/tlru/tree/master) for
a safer, traditional cache implementation.## Basic Usage
The API should be familiar to anyone that's used a map:
```go
// The default value is good to use.
m := weakmap.Map[string, int]{}
m.Set("pi", 3.14)// 3.14, true
v, ok := m.Get("pi")m.Delete("pi")
// It's now gone!
```The map's operations are already protected by a mutex and are safe for concurrent
use.## Eviction
Cache eviction occurs automatically when the GC runs. The number of removed
entries is proportional to the program's memory pressure.Memory pressure is defined as the ratio of [`runtime/metrics`](https://pkg.go.dev/runtime/metrics) `/memory/classes/heap/objects:bytes` to `/memory/classes/total:bytes`. The Map likely needs a tuning
parameter to adjust sensitivity but I'm not yet sure what that should be.See [`gc.go`](./gc.go) for the implementation details.
## Cost Control
Often you want to control the amount of memory a data structure uses even when memory is abundant. To that end, you can specify a
cost function and cost limit on each map. For example:```go
m := Map[string, []byte]{
Coster: func(v []byte) int { return len(v) },
MaxCost: 1024*1024*1024, // 1 GB
}for {
m.Set("big", make([]byte, 1024*1024))
}
// Map will never track more than 1 GB of byte slice memory.
```## Testing
You may run ./example/gctest to see how the map behaves under different
memory conditions.For example:
```bash
$ go run ./example/gctest/ -memlimit 100000000 -allocsize 1000000 -pause 10ms
``````text
allocating at a rate of 100 MB/s to a memory limit of 100 MB
Map Size Total Sets Mem Alloc Next GC GC Runs
13 99 43 MB (42%) 67 MB 7
8 199 49 MB (48%) 85 MB 10
29 299 88 MB (87%) 91 MB 12
36 399 87 MB (86%) 91 MB 15
35 499 75 MB (74%) 85 MB 18
27 599 55 MB (54%) 59 MB 21
34 699 85 MB (84%) 91 MB 24
33 799 73 MB (72%) 85 MB 27
26 900 54 MB (53%) 59 MB 30
32 999 83 MB (82%) 91 MB 33
```## Further Reading
* See https://github.com/golang/go/issues/43615 for the state of the debate of
weak references in Go.