From 6f230f9333e8aa1df61780b266cfeeaf3b693dbc Mon Sep 17 00:00:00 2001 From: phuslu Date: Tue, 26 Mar 2024 21:57:46 +0800 Subject: [PATCH] add gc scan tests --- .github/workflows/gcscan.yml | 44 ++++++++ README.md | 213 +++++++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 .github/workflows/gcscan.yml diff --git a/.github/workflows/gcscan.yml b/.github/workflows/gcscan.yml new file mode 100644 index 0000000..7994f63 --- /dev/null +++ b/.github/workflows/gcscan.yml @@ -0,0 +1,44 @@ +name: gcscan + +on: + schedule: + - cron: '0 0 * * *' + push: + branches: + - master + +jobs: + gcscan: + runs-on: ubuntu-latest + steps: + - name: Install packages + run: sudo apt update -y && sudo apt-get install -y csvkit datamash + - uses: actions/setup-go@v4 + with: + go-version: '1.22' + check-latest: true + - uses: actions/checkout@v4 + - name: go mod tidy + run: | + mkdir bench + cd bench + awk '{if($0 ~ "// env GODEBUG=gctrace=1"){a=1;b=1};if($0 ~ "```" && b=1){b=0};if (a&&b) {print}}' ../README.md > gcscan.go + go mod init bench + go mod tidy + go mod edit -replace github.com/phuslu/lru=../ + cat go.mod + - name: GC Tests + working-directory: ./bench + run: | + echo -e 'GCScan\tCacheSize\tScanTime' > gcscan.txt + for name in nottl phuslu lxzan ristretto freelru ecache otter theine cloudflare ccache hashicorp; do + for cachesize in 100000 200000 400000 1000000; do + env GODEBUG=gctrace=1 repeat=40 go run gcscan.go $name $cachesize 2>&1 | tee gcscan-$name-$cachesize.txt + echo -e "$name\t$cachesize\t$(grep ^gc gcscan-$name-$cachesize.txt | tail -30 | awk -F+ '{a+=$2}END{print int(a/30)}') ms" | tee -a gcscan.txt + done + done + cat gcscan.txt + - name: GC Results + working-directory: ./bench + run: | + cat gcscan.txt | datamash --header-in crosstab 1,2 unique 3 | tee >(head -1) | tail -n +2 | sort -k12 -n | datamash transpose | tee >(head -1) | tail -n +2 | sort -n | datamash transpose | csvlook diff --git a/README.md b/README.md index 11360bb..860d3aa 100644 --- a/README.md +++ b/README.md @@ -491,6 +491,218 @@ PASS ok command-line-arguments 96.989s ``` +### GC scan + +The GC scan result as below. Check github [gcscan][gcscan] action for more results and details. +
+ GC scan on keysize=16(string), valuesize=8(int), cachesize in (100000,200000,400000,1000000) + +```go +// env GODEBUG=gctrace=1 go run gcscan.go phuslu 1000000 +package main + +import ( + "fmt" + "os" + "runtime" + "runtime/debug" + "strconv" + "time" + + theine "github.com/Yiling-J/theine-go" + "github.com/cespare/xxhash/v2" + cloudflare "github.com/cloudflare/golibs/lrucache" + ristretto "github.com/dgraph-io/ristretto" + freelru "github.com/elastic/go-freelru" + hashicorp "github.com/hashicorp/golang-lru/v2/expirable" + ccache "github.com/karlseguin/ccache/v3" + lxzan "github.com/lxzan/memorycache" + otter "github.com/maypok86/otter" + ecache "github.com/orca-zhang/ecache" + phuslu "github.com/phuslu/lru" +) + +const keysize = 16 +var repeat, _ = strconv.ParseBool(os.Getenv("repeat")) + +var keys []string + +func main() { + name := os.Args[1] + cachesize, _ := strconv.Atoi(os.Args[2]) + + keys = make([]string, cachesize) + for i := range cachesize { + keys[i] = fmt.Sprintf(fmt.Sprintf("%%0%dd", keysize), i) + } + + map[string]func(int){ + "nottl": SetupNottl, + "phuslu": SetupPhuslu, + "freelru": SetupFreelru, + "ristretto": SetupRistretto, + "otter": SetupOtter, + "lxzan": SetupLxzan, + "ecache": SetupEcache, + "cloudflare": SetupCloudflare, + "ccache": SetupCcache, + "hashicorp": SetupHashicorp, + "theine": SetupTheine, + }[name](cachesize) +} + +func SetupNottl(cachesize int) { + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + cache := phuslu.NewLRUCache[string, int](cachesize) + runtime.GC() + for range repeat { + for i := range cachesize { + cache.Set(keys[i], i) + } + runtime.GC() + } +} + +func SetupPhuslu(cachesize int) { + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + cache := phuslu.NewTTLCache[string, int](cachesize) + runtime.GC() + for range repeat { + for i := range cachesize { + cache.Set(keys[i], i, time.Hour) + } + runtime.GC() + } +} + +func SetupFreelru(cachesize int) { + cache, _ := freelru.NewSharded[string, int](uint32(cachesize), func(s string) uint32 { return uint32(xxhash.Sum64String(s)) }) + runtime.GC() + for range repeat { + for i := range cachesize { + cache.AddWithLifetime(keys[i], i, time.Hour) + } + runtime.GC() + } +} + +func SetupOtter(cachesize int) { + cache, _ := otter.MustBuilder[string, int](cachesize).WithVariableTTL().Build() + runtime.GC() + for range repeat { + for i := range cachesize { + cache.Set(keys[i], i, time.Hour) + } + runtime.GC() + } +} + +func SetupEcache(cachesize int) { + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + cache := ecache.NewLRUCache(1024, uint16(cachesize/1024), time.Hour) + runtime.GC() + for range repeat { + for i := range cachesize { + cache.Put(keys[i], i) + } + runtime.GC() + } +} + +func SetupRistretto(cachesize int) { + cache, _ := ristretto.NewCache(&ristretto.Config{ + NumCounters: int64(10 * cachesize), // number of keys to track frequency of (10M). + MaxCost: int64(cachesize), // maximum cost of cache (1M). + BufferItems: 64, // number of keys per Get buffer. + }) + runtime.GC() + for range repeat { + for i := range cachesize { + cache.SetWithTTL(keys[i], i, 1, time.Hour) + } + runtime.GC() + } +} + +func SetupLxzan(cachesize int) { + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + cache := lxzan.New[string, int]( + lxzan.WithBucketNum(128), + lxzan.WithBucketSize(cachesize/128, cachesize/128), + lxzan.WithInterval(time.Hour, time.Hour), + ) + runtime.GC() + for range repeat { + for i := range cachesize { + cache.Set(keys[i], i, time.Hour) + } + runtime.GC() + } +} + +func SetupTheine(cachesize int) { + cache, _ := theine.NewBuilder[string, int](int64(cachesize)).Build() + runtime.GC() + for range repeat { + for i := range cachesize { + cache.SetWithTTL(keys[i], i, 1, time.Hour) + } + runtime.GC() + } +} + +func SetupCloudflare(cachesize int) { + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + cache := cloudflare.NewMultiLRUCache(1024, uint(cachesize/1024)) + runtime.GC() + for range repeat { + for i := range cachesize { + cache.Set(keys[i], i, time.Now().Add(time.Hour)) + } + runtime.GC() + } +} + +func SetupCcache(cachesize int) { + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + cache := ccache.New(ccache.Configure[int]().MaxSize(int64(cachesize)).ItemsToPrune(100)) + runtime.GC() + for range repeat { + for i := range cachesize { + cache.Set(keys[i], i, time.Hour) + } + runtime.GC() + } +} + +func SetupHashicorp(cachesize int) { + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + cache := hashicorp.NewLRU[string, int](cachesize, nil, time.Hour) + runtime.GC() + for range repeat { + for i := range cachesize { + cache.Add(keys[i], i) + } + runtime.GC() + } +} +``` +
+ +| GCScan | 100000 | 200000 | 400000 | 1000000 | +| ---------- | ------ | ------ | ------ | ------- | +| nottl | 1 ms | 3 ms | 7 ms | 15 ms | +| phuslu | 1 ms | 3 ms | 6 ms | 15 ms | +| freelru | 2 ms | 3 ms | 6 ms | 16 ms | +| ristretto | 4 ms | 6 ms | 9 ms | 17 ms | +| lxzan | 2 ms | 4 ms | 7 ms | 18 ms | +| cloudflare | 5 ms | 10 ms | 21 ms | 56 ms | +| ccache | 5 ms | 10 ms | 22 ms | 58 ms | +| ecache | 5 ms | 10 ms | 21 ms | 57 ms | +| otter | 6 ms | 12 ms | 28 ms | 64 ms | +| theine | 6 ms | 13 ms | 34 ms | 83 ms | +| hashicorp | 7 ms | 16 ms | 30 ms | 79 ms | + ### Memory usage The Memory usage result as below. Check github [memory][memory] action for more results and details. @@ -684,5 +896,6 @@ For inquiries or support, contact phus.lu@gmail.com or raise github issues. [goreport]: https://goreportcard.com/report/github.com/phuslu/lru [benchmark]: https://github.com/phuslu/lru/actions/workflows/benchmark.yml [memory]: https://github.com/phuslu/lru/actions/workflows/memory.yml +[gcscan]: https://github.com/phuslu/lru/actions/workflows/gcscan.yml [codecov-img]: https://codecov.io/gh/phuslu/lru/graph/badge.svg?token=Q21AMQNM1K [codecov]: https://codecov.io/gh/phuslu/lru