From c215e5862f1c4303a276cb1e3a289b6082c2ac72 Mon Sep 17 00:00:00 2001 From: codingcrush Date: Fri, 5 Jul 2019 15:36:52 +0800 Subject: [PATCH] [feat:#59][pkg:hashers]: allocate-free Fnv-1a --- pkg/hashers/fnv.go | 31 ++++++++++++++++++++++ pkg/hashers/fnv_test.go | 59 +++++++++++++++++++++++++++++++++++++++++ pkg/util/fnv.go | 21 --------------- tsdb/memdb/database.go | 6 ++--- 4 files changed, 93 insertions(+), 24 deletions(-) create mode 100644 pkg/hashers/fnv.go create mode 100644 pkg/hashers/fnv_test.go delete mode 100644 pkg/util/fnv.go diff --git a/pkg/hashers/fnv.go b/pkg/hashers/fnv.go new file mode 100644 index 000000000..7530cd0f4 --- /dev/null +++ b/pkg/hashers/fnv.go @@ -0,0 +1,31 @@ +package hashers + +import ( + "unsafe" +) + +// copy from hash/fnv/fnv.go +const ( + offset32 uint32 = 2166136261 + offset64 uint64 = 14695981039346656037 + prime32 uint32 = 16777619 + prime64 uint64 = 1099511628211 +) + +// Fnv32a returns a 32-bit FNV-1a hash of a string. +func Fnv32a(s string) uint32 { + hash := offset32 + for _, b := range *(*[]byte)(unsafe.Pointer(&s)) { + hash = (hash ^ uint32(b)) * prime32 + } + return hash +} + +// Fnv64a returns a 64-bit FNV-1a hash of a string. +func Fnv64a(s string) uint64 { + hash := offset64 + for _, b := range *(*[]byte)(unsafe.Pointer(&s)) { + hash = (hash ^ uint64(b)) * prime64 + } + return hash +} diff --git a/pkg/hashers/fnv_test.go b/pkg/hashers/fnv_test.go new file mode 100644 index 000000000..cd1094b4f --- /dev/null +++ b/pkg/hashers/fnv_test.go @@ -0,0 +1,59 @@ +package hashers + +import ( + "hash/fnv" + "strconv" + "testing" + + "github.com/stretchr/testify/assert" +) + +var _testString = "abcdefghijklmnopqrstuvwxyz" + +func Test_Fnv32a(t *testing.T) { + for i := 0; i < 100; i++ { + assert.Equal(t, Fnv32a(strconv.Itoa(i)), fnv32a(strconv.Itoa(i))) + } +} + +func Test_Fnv64a(t *testing.T) { + for i := 0; i < 100; i++ { + assert.Equal(t, Fnv64a(strconv.Itoa(i)), fnv64a(strconv.Itoa(i))) + } +} + +func Benchmark_Allocate_Free_FNV32a(b *testing.B) { + for i := 0; i < b.N; i++ { + Fnv32a(_testString) + } +} + +func Benchmark_fnv32a(b *testing.B) { + for i := 0; i < b.N; i++ { + fnv32a(_testString) + } +} + +func Benchmark_Allocate_Free_FNV64(b *testing.B) { + for i := 0; i < b.N; i++ { + Fnv64a(_testString) + } +} + +func Benchmark_fnv64a(b *testing.B) { + for i := 0; i < b.N; i++ { + fnv64a(_testString) + } +} + +func fnv32a(s string) uint32 { + h := fnv.New32a() + _, _ = h.Write([]byte(s)) + return h.Sum32() +} + +func fnv64a(s string) (hash uint64) { + h := fnv.New64a() + _, _ = h.Write([]byte(s)) + return h.Sum64() +} diff --git a/pkg/util/fnv.go b/pkg/util/fnv.go deleted file mode 100644 index e6d22b519..000000000 --- a/pkg/util/fnv.go +++ /dev/null @@ -1,21 +0,0 @@ -package util - -import ( - "hash/fnv" -) - -// todo: @codingcrush, allocate-free - -// Fnv32 returns a 32-bit FNV-1 hash of a string. -func Fnv32(s string) (hash uint32) { - h := fnv.New32a() - _, _ = h.Write([]byte(s)) - return h.Sum32() -} - -// Fnv64 returns a 64-bit FNV-1 hash of a string. -func Fnv64(s string) (hash uint64) { - h := fnv.New64a() - _, _ = h.Write([]byte(s)) - return h.Sum64() -} diff --git a/tsdb/memdb/database.go b/tsdb/memdb/database.go index ecefa1eaf..375d82af9 100644 --- a/tsdb/memdb/database.go +++ b/tsdb/memdb/database.go @@ -6,11 +6,11 @@ import ( "sync" "time" + "github.com/eleme/lindb/pkg/hashers" + radix "github.com/armon/go-radix" "github.com/eleme/lindb/models" - - "github.com/eleme/lindb/pkg/util" ) // MemoryDatabase is a database-like concept of Shard as memTable in cassandra. @@ -69,7 +69,7 @@ func newMemoryDatabase(ctx context.Context) *memoryDatabase { // getBucket returns the mStoresBucket by measurement-name. func (md *memoryDatabase) getBucket(measurement string) *mStoresBucket { - return md.mStoresList[shardingCountMask&util.Fnv32(measurement)] + return md.mStoresList[shardingCountMask&hashers.Fnv32a(measurement)] } // getMeasurementStore returns a TimeSeriesStore by measurement + tags.