mirror of
https://github.com/weaveworks/scope.git
synced 2026-05-21 16:42:59 +00:00
115 lines
3.7 KiB
Markdown
115 lines
3.7 KiB
Markdown
#FreeCache - A cache library for Go with zero GC overhead.
|
|
|
|
Long lived objects in memory introduce expensive GC overhead, the GC latency can go up to hundreds of milliseconds with just a few millions of live objects.
|
|
With FreeCache, you can cache unlimited number of objects in memory without increased GC latency.
|
|
|
|
[](https://travis-ci.org/coocood/freecache)
|
|
[](http://gocover.io/github.com/coocood/freecache)
|
|
[](https://godoc.org/github.com/coocood/freecache)
|
|
|
|
##About GC Pause Issue
|
|
|
|
Here is the demo code for the GC pause issue, you can run it yourself.
|
|
On my laptop, GC pause with FreeCache is under 200us, but with map, it is more than 300ms.
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/coocood/freecache"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"time"
|
|
)
|
|
|
|
var mapCache map[string][]byte
|
|
|
|
func GCPause() time.Duration {
|
|
runtime.GC()
|
|
var stats debug.GCStats
|
|
debug.ReadGCStats(&stats)
|
|
return stats.Pause[0]
|
|
}
|
|
|
|
func main() {
|
|
n := 3000 * 1000
|
|
freeCache := freecache.NewCache(512 * 1024 * 1024)
|
|
debug.SetGCPercent(10)
|
|
for i := 0; i < n; i++ {
|
|
key := fmt.Sprintf("key%v", i)
|
|
val := make([]byte, 10)
|
|
freeCache.Set([]byte(key), val, 0)
|
|
}
|
|
fmt.Println("GC pause with free cache:", GCPause())
|
|
freeCache = nil
|
|
mapCache = make(map[string][]byte)
|
|
for i := 0; i < n; i++ {
|
|
key := fmt.Sprintf("key%v", i)
|
|
val := make([]byte, 10)
|
|
mapCache[key] = val
|
|
}
|
|
fmt.Println("GC pause with map cache:", GCPause())
|
|
}
|
|
```
|
|
|
|
##Features
|
|
* Store hundreds of millions of entries
|
|
* Zero GC overhead
|
|
* High concurrent thread-safe access
|
|
* Pure Go implementation
|
|
* Expiration support
|
|
* Nearly LRU algorithm
|
|
* Strictly limited memory usage
|
|
* Come with a toy server that supports a few basic Redis commands with pipeline
|
|
|
|
##Performance
|
|
Here is the benchmark result compares to built-in map, `Set` performance is about 2x faster than built-in map, `Get` performance is about 1/2x slower than built-in map. Since it is single threaded benchmark, in multi-threaded environment,
|
|
FreeCache should be many times faster than single lock protected built-in map.
|
|
|
|
BenchmarkCacheSet 3000000 446 ns/op
|
|
BenchmarkMapSet 2000000 861 ns/op
|
|
BenchmarkCacheGet 3000000 517 ns/op
|
|
BenchmarkMapGet 10000000 212 ns/op
|
|
|
|
##Example Usage
|
|
```go
|
|
cacheSize := 100 * 1024 * 1024
|
|
cache := freecache.NewCache(cacheSize)
|
|
debug.SetGCPercent(20)
|
|
key := []byte("abc")
|
|
val := []byte("def")
|
|
expire := 60 // expire in 60 seconds
|
|
cache.Set(key, val, expire)
|
|
got, err := cache.Get(key)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
} else {
|
|
fmt.Println(string(got))
|
|
}
|
|
affected := cache.Del(key)
|
|
fmt.Println("deleted key ", affected)
|
|
fmt.Println("entry count ", cache.EntryCount())
|
|
```
|
|
|
|
##Notice
|
|
* Recommended Go version is 1.4.
|
|
* Memory is preallocated.
|
|
* If you allocate large amount of memory, you may need to set `debug.SetGCPercent()`
|
|
to a much lower percentage to get a normal GC frequency.
|
|
|
|
##How it is done
|
|
FreeCache avoids GC overhead by reducing the number of pointers.
|
|
No matter how many entries stored in it, there are only 512 pointers.
|
|
The data set is sharded into 256 segments by the hash value of the key.
|
|
Each segment has only two pointers, one is the ring buffer that stores keys and values,
|
|
the other one is the index slice which used to lookup for an entry.
|
|
Each segment has its own lock, so it supports high concurrent access.
|
|
|
|
##TODO
|
|
* Support dump to file and load from file.
|
|
* Support resize cache size at runtime.
|
|
|
|
##License
|
|
The MIT License
|