From a5fb792dcf0ccf852493aea88125c4fba15125c6 Mon Sep 17 00:00:00 2001 From: charlie <3140647@qq.com> Date: Mon, 28 Mar 2022 17:35:58 +0800 Subject: [PATCH] fnv --- collections/generics/concurrent_map.go | 46 +++++++++-- collections/generics/concurrent_map_test.go | 85 +++++++++++++++++++++ collections/generics/map_test.go | 28 +++++++ collections/generics/rwlock_map.go | 11 +-- 4 files changed, 158 insertions(+), 12 deletions(-) diff --git a/collections/generics/concurrent_map.go b/collections/generics/concurrent_map.go index 3e1982d..a848c6a 100644 --- a/collections/generics/concurrent_map.go +++ b/collections/generics/concurrent_map.go @@ -4,8 +4,6 @@ import ( "fmt" "runtime" "sync" - - "github.com/charlienet/go-mixed/hash" ) var _ Map[string, string] = &ConcurrnetMap[string, string]{} @@ -91,12 +89,46 @@ func (m *ConcurrnetMap[K, V]) Count() int { } func (m *ConcurrnetMap[K, V]) getBucket(k K) Map[K, V] { - bytes := getBytes(k) - - id := hash.XXHashUint64(bytes) % m.numOfBuckets + id := getTag(k) % m.numOfBuckets return m.buckets[id] } -func getBytes(k any) []byte { - return []byte(fmt.Sprintf("%v", k)) +func getTag[T comparable](v T) uint64 { + var vv any = v + + switch vv.(type) { + case string: + return fnv64(vv.(string)) + case int8: + return uint64(vv.(int8)) + case uint8: + return uint64(vv.(uint8)) + case int: + return uint64(vv.(int)) + case int32: + return uint64(vv.(int32)) + case uint32: + return uint64(vv.(uint32)) + case int64: + return uint64(vv.(int64)) + case uint64: + return vv.(uint64) + default: + return fnv64(fmt.Sprintf("%v", v)) + } +} + +const ( + prime32 = uint64(16777619) +) + +func fnv64(k string) uint64 { + var hash = uint64(2166136261) + l := len(k) + for i := 0; i < l; i++ { + hash *= prime32 + hash ^= uint64(k[i]) + } + + return hash } diff --git a/collections/generics/concurrent_map_test.go b/collections/generics/concurrent_map_test.go index 3b4aa97..6d46495 100644 --- a/collections/generics/concurrent_map_test.go +++ b/collections/generics/concurrent_map_test.go @@ -5,7 +5,9 @@ import ( "runtime" "testing" + "github.com/charlienet/go-mixed/bytesconv" "github.com/charlienet/go-mixed/collections/generics" + "github.com/charlienet/go-mixed/hash" ) func TestConcurrentMap(t *testing.T) { @@ -81,3 +83,86 @@ func BenchmarkRWLockMap(b *testing.B) { } }) } + +func BenchmarkGetIndex(b *testing.B) { + + b.Run("字符串-Sprintf", func(b *testing.B) { + v := "abc" + for i := 0; i < b.N; i++ { + bytes := []byte(fmt.Sprintf("%v", v)) + hash.XXHashUint64(bytes) + } + }) + + b.Run("字符串", func(b *testing.B) { + v := "abc" + for i := 0; i < b.N; i++ { + getTag(v) + } + }) + + b.Run("数字", func(b *testing.B) { + v := 124 + for i := 0; i < b.N; i++ { + getTag(v) + } + }) +} + +func getTag[T comparable](v T) uint64 { + var vv any = v + + switch vv.(type) { + case string: + return getStringIndex([]byte(vv.(string))) + case int8: + return uint64(vv.(int8)) + case uint8: + return uint64(vv.(uint8)) + case int: + return uint64(vv.(int)) + case int32: + return uint64(vv.(int32)) + case uint32: + return uint64(vv.(uint32)) + case int64: + return uint64(vv.(int64)) + case uint64: + return vv.(uint64) + default: + return getStringIndex([]byte(fmt.Sprintf("%v", v))) + } +} + +func getStringIndex(v []byte) uint64 { + if len(v) > 4 { + v = v[:4] + } + + // return hash.XXHashUint64(v) + i, _ := bytesconv.BigEndian.BytesToUInt64(v) + return i +} + +func TestFnv64(t *testing.T) { + t.Log(fnv64("a")) + t.Log(fnv64("b")) + t.Log(fnv64("c")) + t.Log(fnv64("d")) + t.Log(fnv64("e")) +} + +const ( + prime32 = uint64(16777619) +) + +func fnv64(k string) uint64 { + var hash = uint64(2166136261) + l := len(k) + for i := 0; i < l; i++ { + hash *= prime32 + hash ^= uint64(k[i]) + } + + return hash +} diff --git a/collections/generics/map_test.go b/collections/generics/map_test.go index 371287a..047ef81 100644 --- a/collections/generics/map_test.go +++ b/collections/generics/map_test.go @@ -29,3 +29,31 @@ func TestMapCount(t *testing.T) { mm["a"] = "b" assert.Equal(t, 1, len(mm)) } + +func BenchmarkMap(b *testing.B) { + b.Run("RWLock", func(b *testing.B) { + m := generics.NewRWLockMap[string, string]() + doBenchamark(b, m) + + }) + + b.Run("ConcurrnetMap", func(b *testing.B) { + doBenchamark(b, generics.NewConcurrnetMap[string, string]()) + }) +} + +func doBenchamark(b *testing.B, m generics.Map[string, string]) { + var k = "abc" + var v = "bcd" + + b.RunParallel(func(p *testing.PB) { + for p.Next() { + m.Set(k, v) + m.Get(k) + m.Get(k) + m.Get(k) + m.Delete(k) + } + }) + +} diff --git a/collections/generics/rwlock_map.go b/collections/generics/rwlock_map.go index 119157d..cd7aaab 100644 --- a/collections/generics/rwlock_map.go +++ b/collections/generics/rwlock_map.go @@ -1,6 +1,10 @@ package generics -import "sync" +import ( + "sync" + + "golang.org/x/exp/maps" +) var _ Map[string, string] = &RWLockMap[string, string]{} @@ -41,10 +45,7 @@ func (m *RWLockMap[K, V]) ForEach(f func(K, V)) { } func (m *RWLockMap[K, V]) Clone() Map[K, V] { - new := make(map[K]V, m.Count()) - for k, v := range m.m { - new[k] = v - } + new := maps.Clone(m.m) return &RWLockMap[K, V]{ m: new,