From 4e0934e2d648638c3dd35ff85b6e8e47c1d42dc1 Mon Sep 17 00:00:00 2001 From: David Finkel Date: Tue, 2 Aug 2022 18:19:47 -0400 Subject: [PATCH] marginal improvement: atomic byte-counts Eliminate an extra lock/unlock on the eviction path by making the `nbytes` field atomic. This produces some marginal performance improvements. e.g. here are the benchmark results from my laptop: ``` [atomic_bytecount] % go test -benchtime 30s -bench . . goos: linux goarch: amd64 pkg: github.com/vimeo/galaxycache cpu: Intel(R) Core(TM) i7-7600U CPU @ 2.80GHz BenchmarkGetsSerialOneKey-4 19998571 1849 ns/op 1536 B/op 28 allocs/op BenchmarkGetsSerialManyKeys-4 10693790 3401 ns/op 2394 B/op 46 allocs/op BenchmarkGetsParallelManyKeys-4 12335263 2872 ns/op 2381 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/1-4 12733614 2967 ns/op 2382 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/2-4 12099810 2755 ns/op 2379 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/4-4 13770105 2689 ns/op 2378 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/8-4 13695392 2717 ns/op 2374 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/16-4 12850902 2745 ns/op 2367 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/32-4 13667977 2749 ns/op 2356 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/64-4 13518266 2917 ns/op 2330 B/op 44 allocs/op PASS ok github.com/vimeo/galaxycache 398.172s go test -benchtime 30s -bench . . 1201.40s user 37.27s system 309% cpu 6:39.64 total 2022-08-03 11:55:31 ($? = 0) [master] % go test -benchtime 30s -bench . . goos: linux goarch: amd64 pkg: github.com/vimeo/galaxycache cpu: Intel(R) Core(TM) i7-7600U CPU @ 2.80GHz BenchmarkGetsSerialOneKey-4 20515902 1852 ns/op 1536 B/op 28 allocs/op BenchmarkGetsSerialManyKeys-4 10534585 3448 ns/op 2395 B/op 46 allocs/op BenchmarkGetsParallelManyKeys-4 11663724 2968 ns/op 2382 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/1-4 12227192 2956 ns/op 2380 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/2-4 12722972 2972 ns/op 2378 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/4-4 10123365 3382 ns/op 2379 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/8-4 11134406 3433 ns/op 2375 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/16-4 12949555 3012 ns/op 2367 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/32-4 12999852 2946 ns/op 2351 B/op 45 allocs/op BenchmarkGetsParallelManyKeysWithGoroutines/64-4 13319569 3028 ns/op 2325 B/op 44 allocs/op PASS ok github.com/vimeo/galaxycache 406.806s go test -benchtime 30s -bench . . 1166.79s user 37.50s system 295% cpu 6:47.19 total ``` --- galaxycache.go | 8 +++----- galaxycache_test.go | 4 ++-- typed_caches.go | 4 ++-- untyped_caches.go | 4 ++-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/galaxycache.go b/galaxycache.go index b8a2046e..a81460c5 100644 --- a/galaxycache.go +++ b/galaxycache.go @@ -704,7 +704,7 @@ func (c *cache) stats() CacheStats { c.mu.Lock() defer c.mu.Unlock() return CacheStats{ - Bytes: c.nbytes, + Bytes: c.nbytes.Get(), Items: c.itemsLocked(), Gets: c.nget, Hits: c.nhit, @@ -732,7 +732,7 @@ func (c *cache) add(key string, value valWithStat) { c.mu.Lock() defer c.mu.Unlock() c.lru.Add(key, value) - c.nbytes += int64(len(key)) + value.size() + c.nbytes.Add(int64(len(key)) + value.size()) } func (c *cache) removeOldest() { @@ -745,9 +745,7 @@ func (c *cache) removeOldest() { } func (c *cache) bytes() int64 { - c.mu.Lock() - defer c.mu.Unlock() - return c.nbytes + return c.nbytes.Get() } func (c *cache) items() int64 { diff --git a/galaxycache_test.go b/galaxycache_test.go index 33c0b5af..0445606e 100644 --- a/galaxycache_test.go +++ b/galaxycache_test.go @@ -388,8 +388,8 @@ func TestNoDedup(t *testing.T) { testKStats := keyStats{dQPS: windowedAvgQPS{}} testvws := g.newValWithStat([]byte(testval), &testKStats) wantBytes := int64(len(testkey)) + testvws.size() - if g.mainCache.nbytes != wantBytes { - t.Errorf("cache has %d bytes, want %d", g.mainCache.nbytes, wantBytes) + if g.mainCache.nbytes.Get() != wantBytes { + t.Errorf("cache has %d bytes, want %d", g.mainCache.nbytes.Get(), wantBytes) } } diff --git a/typed_caches.go b/typed_caches.go index 9b1edc2f..2ba6acaa 100644 --- a/typed_caches.go +++ b/typed_caches.go @@ -53,7 +53,7 @@ func (c *candidateCache) get(key string) (*keyStats, bool) { type cache struct { mu sync.Mutex lru *lru.TypedCache[string, valWithStat] - nbytes int64 // of all keys and values + nbytes AtomicInt // of all keys and values nhit, nget int64 nevict int64 // number of evictions ctype CacheType @@ -69,7 +69,7 @@ func newCache(kind CacheType) cache { func (c *cache) setLRUOnEvicted(f func(key string, kStats *keyStats)) { c.lru.OnEvicted = func(key string, value valWithStat) { val := value - c.nbytes -= int64(len(key)) + val.size() + c.nbytes.Add(-(int64(len(key)) + val.size())) c.nevict++ if f != nil { f(key, val.stats) diff --git a/untyped_caches.go b/untyped_caches.go index 430bb59a..59b64e02 100644 --- a/untyped_caches.go +++ b/untyped_caches.go @@ -57,7 +57,7 @@ func (c *candidateCache) get(key string) (*keyStats, bool) { type cache struct { mu sync.Mutex lru *lru.Cache - nbytes int64 // of all keys and values + nbytes AtomicInt // of all keys and values nhit, nget int64 nevict int64 // number of evictions ctype CacheType @@ -73,7 +73,7 @@ func newCache(kind CacheType) cache { func (c *cache) setLRUOnEvicted(f func(key string, kStats *keyStats)) { c.lru.OnEvicted = func(key lru.Key, value interface{}) { val := value.(valWithStat) - c.nbytes -= int64(len(key.(string))) + val.size() + c.nbytes.Add(-(int64(len(key.(string))) + val.size())) c.nevict++ if f != nil { f(key.(string), val.stats)