From 52456ac80b11263288d0c2cd028ba08965ed8b19 Mon Sep 17 00:00:00 2001 From: jimchen Date: Thu, 22 Aug 2024 19:02:20 +0800 Subject: [PATCH] Benchmark --- benchmark/lru_test.go | 224 ++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 6 + internal/deepcopy/deepcopy.go | 23 ++++ 4 files changed, 254 insertions(+) create mode 100644 benchmark/lru_test.go diff --git a/benchmark/lru_test.go b/benchmark/lru_test.go new file mode 100644 index 0000000..3b35819 --- /dev/null +++ b/benchmark/lru_test.go @@ -0,0 +1,224 @@ +package benchmark + +import ( + "testing" + + "github.com/lovelysunlight/lru-go" + "github.com/pioz/faker" +) + +type Object struct { + Str1 string + Str2 string + Str3 string + Str4 string + Age1 int + Age2 int + Age3 int + Age4 int + Bool1 bool + Bool2 bool + Bool3 bool + Bool4 bool + + Obj1 *Object + Obj2 *Object + Obj3 *Object + Obj4 *Object + + Map1 map[string]string + Map2 map[string]int + Map3 map[string]bool + Map4 map[string]float32 + Map5 map[string]*Object +} + +func BenchmarkLruCache_Put(b *testing.B) { + b.Run("int", func(b *testing.B) { + cache, _ := lru.New[int, int](10) + for i := 0; i < b.N; i++ { + cache.Put(i, i) + } + }) + + b.Run("float", func(b *testing.B) { + cache, _ := lru.New[int, float32](10) + for i := 0; i < b.N; i++ { + cache.Push(i, 1.1) + } + }) + + b.Run("bool", func(b *testing.B) { + cache, _ := lru.New[int, bool](10) + for i := 0; i < b.N; i++ { + cache.Push(i, true) + } + }) + + b.Run("string", func(b *testing.B) { + cache, _ := lru.New[int, string](10) + for i := 0; i < b.N; i++ { + cache.Push(i, "aaaaa") + } + }) + + b.Run("struct", func(b *testing.B) { + cache, _ := lru.New[int, *Object](10) + for i := 0; i < b.N; i++ { + cache.Push(i, &Object{ + Str1: faker.String(), + Str2: faker.String(), + Str3: faker.String(), + Str4: faker.String(), + Age1: faker.Int(), + Age2: faker.Int(), + Age3: faker.Int(), + Age4: faker.Int(), + Bool1: faker.Bool(), + Bool2: faker.Bool(), + Bool3: faker.Bool(), + Bool4: faker.Bool(), + + Obj1: &Object{}, + Obj2: &Object{}, + Obj3: &Object{}, + Obj4: nil, + + Map1: map[string]string{ + faker.String(): faker.String(), + faker.String(): faker.String(), + faker.String(): faker.String(), + faker.String(): faker.String(), + }, + Map2: map[string]int{ + faker.String(): faker.Int(), + }, + Map3: map[string]bool{ + faker.String(): faker.Bool(), + }, + Map4: map[string]float32{ + faker.String(): faker.Float32(), + }, + }) + } + }) +} + +func BenchmarkLruCache_Get(b *testing.B) { + b.Run("int", func(b *testing.B) { + cache, _ := lru.New[int, int](10) + cache.Put(1, 1) + for i := 0; i < b.N; i++ { + cache.Get(1) + } + }) + + b.Run("float", func(b *testing.B) { + cache, _ := lru.New[int, float32](10) + cache.Put(1, 1.1) + for i := 0; i < b.N; i++ { + cache.Get(1) + } + }) + + b.Run("bool", func(b *testing.B) { + cache, _ := lru.New[int, bool](10) + cache.Put(1, true) + for i := 0; i < b.N; i++ { + cache.Get(1) + } + }) + + b.Run("string", func(b *testing.B) { + cache, _ := lru.New[int, string](10) + cache.Put(1, "aaaaa") + for i := 0; i < b.N; i++ { + cache.Get(1) + } + }) +} + +func BenchmarkLruCache_Peek(b *testing.B) { + b.Run("int", func(b *testing.B) { + cache, _ := lru.New[int, int](10) + cache.Put(1, 1) + for i := 0; i < b.N; i++ { + cache.Peek(1) + } + }) + + b.Run("float", func(b *testing.B) { + cache, _ := lru.New[int, float32](10) + cache.Put(1, 1.1) + for i := 0; i < b.N; i++ { + cache.Peek(1) + } + }) + + b.Run("bool", func(b *testing.B) { + cache, _ := lru.New[int, bool](10) + cache.Put(1, true) + for i := 0; i < b.N; i++ { + cache.Peek(1) + } + }) + + b.Run("string", func(b *testing.B) { + cache, _ := lru.New[int, string](10) + cache.Put(1, "aaaaa") + for i := 0; i < b.N; i++ { + cache.Peek(1) + } + }) +} + +func BenchmarkLruCache_Pop(b *testing.B) { + len := 10000000 + b.Run("int", func(b *testing.B) { + cache, _ := lru.New[int, int](len) + for i := 0; i < len; i++ { + cache.Put(i, i) + } + b.Run("benchmark", func(b *testing.B) { + for i := 0; i < b.N; i++ { + cache.Pop(1) + } + }) + }) + + b.Run("float", func(b *testing.B) { + cache, _ := lru.New[int, float32](len) + for i := 0; i < len; i++ { + cache.Put(i, 1.1) + } + b.Run("benchmark", func(b *testing.B) { + for i := 0; i < b.N; i++ { + cache.Pop(1) + } + }) + }) + + b.Run("bool", func(b *testing.B) { + cache, _ := lru.New[int, bool](len) + for i := 0; i < len; i++ { + cache.Put(i, true) + } + b.Run("benchmark", func(b *testing.B) { + for i := 0; i < b.N; i++ { + cache.Pop(1) + } + }) + }) + + b.Run("string", func(b *testing.B) { + cache, _ := lru.New[int, string](len) + for i := 0; i < len; i++ { + cache.Put(i, "aaaaa") + } + b.Run("benchmark", func(b *testing.B) { + for i := 0; i < b.N; i++ { + cache.Pop(1) + } + }) + }) +} diff --git a/go.mod b/go.mod index d91b558..c76312e 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require github.com/stretchr/testify v1.9.0 require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pioz/faker v1.7.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 60ce688..a367811 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,16 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pioz/faker v1.7.3 h1:Tez8Emuq0UN+/d6mo3a9m/9ZZ/zdfJk0c5RtRatrceM= +github.com/pioz/faker v1.7.3/go.mod h1:xSpay5w/oz1a6+ww0M3vfpe40pSIykeUPeWEc3TvVlc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/deepcopy/deepcopy.go b/internal/deepcopy/deepcopy.go index 52c1c79..8338c8f 100644 --- a/internal/deepcopy/deepcopy.go +++ b/internal/deepcopy/deepcopy.go @@ -18,6 +18,8 @@ func Iface(iface interface{}) interface{} { // Copy creates a deep copy of whatever is passed to it and returns the copy // in an interface{}. The returned value will need to be asserted to the // correct type. +// +//go:inline func Copy[T any](src T) T { var empty T if reflect.DeepEqual(src, empty) { @@ -27,6 +29,11 @@ func Copy[T any](src T) T { // Make the interface a reflect.Value original := reflect.ValueOf(src) + // If it's a basic type, we don't need to do anything special. + if isBasicType(original.Kind()) { + return src + } + // Make a copy of the same type as the original. cpy := reflect.New(original.Type()).Elem() @@ -37,6 +44,22 @@ func Copy[T any](src T) T { return cpy.Interface().(T) } +func isBasicType(kind reflect.Kind) bool { + switch kind { + case reflect.Bool: + fallthrough + case reflect.String: + fallthrough + case reflect.Float32, reflect.Float64: + fallthrough + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + default: + return false + } +} + // copyRecursive does the actual copying of the interface. It currently has // limited support for what it can handle. Add as needed. func copyRecursive(original, cpy reflect.Value) {