From 3c4bc381f3b754bb58d1890a4316bdac0c991b36 Mon Sep 17 00:00:00 2001 From: maypok86 Date: Thu, 11 Jan 2024 01:04:39 +0300 Subject: [PATCH] [Chore] Add a description of throughput benchmarks --- README.md | 114 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index b0b8457..373249d 100644 --- a/README.md +++ b/README.md @@ -74,35 +74,35 @@ Otter uses a builder pattern that allows you to conveniently create a cache obje package main import ( - "github.com/maypok86/otter" + "github.com/maypok86/otter" ) func main() { - // NewBuilder creates a builder and sets the future cache capacity to 1000 elements. - // Returns an error if capacity <= 0. - builder, err := otter.NewBuilder[string, string](1000) - if err != nil { - panic(err) - } - - // StatsEnabled determines whether statistics should be calculated when the cache is running. - // By default, statistics calculating is disabled. - builder.StatsEnabled(true) - - // Cost sets a function to dynamically calculate the weight of a key-value pair. - // By default this function always returns 1. - builder.Cost(func(key string, value string) uint32 { - return uint32(len(value)) - }) - - // Build creates a new cache object or - // returns an error if invalid parameters were passed to the builder. - cache, err := builder.Build() - if err != nil { - panic(err) - } - - cache.Close() + // NewBuilder creates a builder and sets the future cache capacity to 1000 elements. + // Returns an error if capacity <= 0. + builder, err := otter.NewBuilder[string, string](1000) + if err != nil { + panic(err) + } + + // StatsEnabled determines whether statistics should be calculated when the cache is running. + // By default, statistics calculating is disabled. + builder.StatsEnabled(true) + + // Cost sets a function to dynamically calculate the weight of a key-value pair. + // By default this function always returns 1. + builder.Cost(func(key string, value string) uint32 { + return uint32(len(value)) + }) + + // Build creates a new cache object or + // returns an error if invalid parameters were passed to the builder. + cache, err := builder.Build() + if err != nil { + panic(err) + } + + cache.Close() } ``` @@ -111,34 +111,34 @@ func main() { package main import ( - "fmt" - "time" + "fmt" + "time" - "github.com/maypok86/otter" + "github.com/maypok86/otter" ) func main() { - // create a cache with capacity equal to 10000 elements - cache, err := otter.MustBuilder[string, string](10_000).Build() - if err != nil { - panic(err) - } - - // set key-value pair with ttl (1 hour) - cache.SetWithTTL("key", "value", time.Hour) - - // get value from cache - value, ok := cache.Get("key") - if !ok { - panic("not found key") - } - fmt.Println(value) - - // delete key-value pair from cache - cache.Delete("key") - - // delete data and stop goroutines - cache.Close() + // create a cache with capacity equal to 10000 elements + cache, err := otter.MustBuilder[string, string](10_000).Build() + if err != nil { + panic(err) + } + + // set key-value pair with ttl (1 hour) + cache.SetWithTTL("key", "value", time.Hour) + + // get value from cache + value, ok := cache.Get("key") + if !ok { + panic("not found key") + } + fmt.Println(value) + + // delete key-value pair from cache + cache.Delete("key") + + // delete data and stop goroutines + cache.Close() } ``` @@ -148,26 +148,40 @@ The benchmark code can be found [here](https://github.com/maypok86/benchmarks) ### 🚀 Performance +Throughput benchmarks are a port of the caffeine [benchmarks](https://github.com/ben-manes/caffeine/blob/master/caffeine/src/jmh/java/com/github/benmanes/caffeine/cache/GetPutBenchmark.java) in Golang. + #### Read (100%) +In this [benchmark](https://github.com/maypok86/benchmarks/blob/main/perf/bench_test.go) **8 threads** concurrently read from a cache configured with a maximum size. + reads=100%,writes=0% #### Read (75%) / Write (25%) +In this [benchmark](https://github.com/maypok86/benchmarks/blob/main/perf/bench_test.go) **6 threads** concurrently read from and **2 threads** write to a cache configured with a maximum size. + reads=75%,writes=25% #### Read (50%) / Write (50%) +In this [benchmark](https://github.com/maypok86/benchmarks/blob/main/perf/bench_test.go) **4 threads** concurrently read from and **4 threads** write to a cache configured with a maximum size. + reads=50%,writes=50% #### Read (25%) / Write (75%) +In this [benchmark](https://github.com/maypok86/benchmarks/blob/main/perf/bench_test.go) **2 threads** concurrently read from and **6 threads** write to a cache configured with a maximum size. + reads=25%,writes=75% #### Write (100%) +In this [benchmark](https://github.com/maypok86/benchmarks/blob/main/perf/bench_test.go) **8 threads** concurrently write to a cache configured with a maximum size. + reads=0%,writes=100% +Otter shows fantastic speed under all loads except extreme write-heavy, but such a load is very rare for caches and usually indicates that the cache has a very small hit ratio. + ### 🎯 Hit ratio #### Zipf