From fe42246f697666b69cdb09b8f0250e904e7ec787 Mon Sep 17 00:00:00 2001 From: Derek Collison Date: Mon, 29 Oct 2012 20:20:33 -0700 Subject: [PATCH] Initial hash commit --- .gitignore | 10 +++ README.md | 2 +- hash/hash.go | 207 ++++++++++++++++++++++++++++++++++++++++++++++ hash/hash_test.go | 94 +++++++++++++++++++++ hash/results.txt | 75 +++++++++++++++++ 5 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 hash/hash.go create mode 100644 hash/hash_test.go create mode 100644 hash/results.txt diff --git a/.gitignore b/.gitignore index 00268614..8ef32918 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,13 @@ _cgo_export.* _testmain.go *.exe + +# Emacs +*~ +\#*\# +.\#* + +# Mac +.DS_Store + +# bin diff --git a/README.md b/README.md index eff20799..5b2281a9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ gnatsd ====== -High Performance NATS Server \ No newline at end of file +High Performance NATS Server written in Go diff --git a/hash/hash.go b/hash/hash.go new file mode 100644 index 00000000..c1c9c23a --- /dev/null +++ b/hash/hash.go @@ -0,0 +1,207 @@ +// Copyright 2012 Apcera Inc. All rights reserved. + +// Collection of high performance 32-bit hash functions. +package hash + +import ( + "unsafe" +) + +// Constants defined by the Murmur3 algorithm +const ( + _C1 = uint32(0xcc9e2d51) + _C2 = uint32(0x1b873593) + _F1 = uint32(0x85ebca6b) + _F2 = uint32(0xc2b2ae35) +) + +// A default seed for Murmur3 +const M3Seed = uint32(0x9747b28c) + +// Generates a Murmur3 Hash [http://code.google.com/p/smhasher/wiki/MurmurHash3] +// Does not generate intermediate objects. +func Murmur3(data []byte, seed uint32) uint32 { + h1 := seed + ldata := len(data) + end := ldata - (ldata % 4) + i := 0 + + // Inner + for ; i < end; i += 4 { + k1 := *(*uint32)(unsafe.Pointer(&data[i])) + k1 *= _C1 + k1 = (k1 << 15) | (k1 >> 17) + k1 *= _C2 + + h1 ^= k1 + h1 = (h1 << 13) | (h1 >> 19) + h1 = h1*5 + 0xe6546b64 + } + + // Tail + var k1 uint32 + switch ldata - i { + case 3: + k1 |= uint32(data[i+2]) << 16 + fallthrough + case 2: + k1 |= uint32(data[i+1]) << 8 + fallthrough + case 1: + k1 |= uint32(data[i]) + k1 *= _C1 + k1 = (k1 << 15) | (k1 >> 17) + k1 *= _C2 + h1 ^= k1 + } + + // Finalization + h1 ^= uint32(ldata) + h1 ^= (h1 >> 16) + h1 *= _F1 + h1 ^= (h1 >> 13) + h1 *= _F2 + h1 ^= (h1 >> 16) + + return h1 +} + +// Generates a Bernstein Hash. +func Bernstein(data []byte) uint32 { + hash := uint32(5381) + for _, b := range data { + hash = ((hash << 5) + hash) + uint32(b) + } + return hash +} + +// Constants for FNV1A and derivatives +const ( + _OFF32 = 2166136261 + _P32 = 16777619 + _YP32 = 709607 +) + +// Generates an FNV1A Hash [http://en.wikipedia.org/wiki/Fowle-Noll-Vo_hash_function] +func FNV1A(data []byte) uint32 { + var hash uint32 = _OFF32 + for _, c := range data { + hash ^= uint32(c) + hash *= _P32 + } + return hash +} + +// Constants for multiples of sizeof(WORD) +const ( + _WSZ = 4 // 4 + _DWSZ = _WSZ << 1 // 8 + _DDWSZ = _WSZ << 2 // 16 + _DDDWSZ = _WSZ << 3 // 32 +) + +// Jesteress derivative of FNV1A from [http://www.sanmayce.com/Fastest_Hash/] +func Jesteress(data []byte) uint32 { + h32 := uint32(_OFF32) + dlen := len(data) + i := 0 + + for ; dlen >= _DDWSZ; dlen -= _DDWSZ { + k1 := *(*uint64)(unsafe.Pointer(&data[i])) + k2 := *(*uint64)(unsafe.Pointer(&data[i+4])) + h32 = uint32((uint64(h32) ^ ((k1<<5 | k1>>27) ^ k2)) * _YP32) + i += _DDWSZ + } + + // Cases: 0,1,2,3,4,5,6,7 + if (dlen & _DWSZ) > 0 { + k1 := *(*uint64)(unsafe.Pointer(&data[i])) + h32 = uint32(uint64(h32) ^ k1) * _YP32 + i += _DWSZ + } + if (dlen & _WSZ) > 0 { + k1 := *(*uint32)(unsafe.Pointer(&data[i])) + h32 = (h32 ^ k1) * _YP32 + i += _WSZ + } + if (dlen & 1) > 0 { + h32 = (h32 ^ uint32(data[i])) * _YP32 + } + return h32 ^ (h32 >> 16) +} + +// Meiyan derivative of FNV1A from [http://www.sanmayce.com/Fastest_Hash/] +func Meiyan(data []byte) uint32 { + h32 := uint32(_OFF32) + dlen := len(data) + i := 0 + + for ; dlen >= _DDWSZ; dlen -= _DDWSZ { + k1 := *(*uint64)(unsafe.Pointer(&data[i])) + k2 := *(*uint64)(unsafe.Pointer(&data[i+4])) + h32 = uint32((uint64(h32) ^ ((k1<<5 | k1>>27) ^ k2)) * _YP32) + i += _DDWSZ + } + + // Cases: 0,1,2,3,4,5,6,7 + if (dlen & _DWSZ) > 0 { + k1 := *(*uint64)(unsafe.Pointer(&data[i])) + h32 = uint32(uint64(h32) ^ k1) * _YP32 + i += _WSZ + k1 = *(*uint64)(unsafe.Pointer(&data[i])) + h32 = uint32(uint64(h32) ^ k1) * _YP32 + i += _WSZ + } + if (dlen & _WSZ) > 0 { + k1 := *(*uint32)(unsafe.Pointer(&data[i])) + h32 = (h32 ^ k1) * _YP32 + i += _WSZ + } + if (dlen & 1) > 0 { + h32 = (h32 ^ uint32(data[i])) * _YP32 + } + return h32 ^ (h32 >> 16) +} + +// Yorikke derivative of FNV1A from [http://www.sanmayce.com/Fastest_Hash/] +func Yorikke(data []byte) uint32 { + h32 := uint32(_OFF32) + h32b := uint32(_OFF32) + dlen := len(data) + i := 0 + + for ; dlen >= _DDDWSZ; dlen -= _DDDWSZ { + k1 := *(*uint64)(unsafe.Pointer(&data[i])) + k2 := *(*uint64)(unsafe.Pointer(&data[i+4])) + h32 = uint32((uint64(h32) ^ (((k1<<5 | k1>>27)) ^ k2)) * _YP32) + k1 = *(*uint64)(unsafe.Pointer(&data[i+8])) + k2 = *(*uint64)(unsafe.Pointer(&data[i+12])) + h32b = uint32((uint64(h32b) ^ (((k1<<5 | k1>>27)) ^ k2)) * _YP32) + i += _DDDWSZ + } + if (dlen & _DDWSZ) > 0 { + k1 := *(*uint64)(unsafe.Pointer(&data[i])) + k2 := *(*uint64)(unsafe.Pointer(&data[i+4])) + h32 = uint32((uint64(h32) ^ k1) * _YP32) + h32b = uint32((uint64(h32b) ^ k2) * _YP32) + i += _DDWSZ + } + // Cases: 0,1,2,3,4,5,6,7 + if (dlen & _DWSZ) > 0 { + k1 := *(*uint32)(unsafe.Pointer(&data[i])) + k2 := *(*uint32)(unsafe.Pointer(&data[i+2])) + h32 = (h32 ^ k1) * _YP32 + h32b = (h32b ^ k2) * _YP32 + i += _DWSZ + } + if (dlen & _WSZ) > 0 { + k1 := *(*uint32)(unsafe.Pointer(&data[i])) + h32 = (h32 ^ k1) * _YP32 + i += _WSZ + } + if (dlen & 1) > 0 { + h32 = (h32 ^ uint32(data[i])) * _YP32; + } + h32 = (h32 ^ (h32b<<5 | h32b>>27)) * _YP32 + return h32 ^ (h32 >> 16) +} diff --git a/hash/hash_test.go b/hash/hash_test.go new file mode 100644 index 00000000..11471ec7 --- /dev/null +++ b/hash/hash_test.go @@ -0,0 +1,94 @@ +package hash + +import ( + "testing" +) + +var foo = []byte("foo") +var bar = []byte("bar") +var baz = []byte("baz") +var sub = []byte("apcera.continuum.router.foo.bar") + +func Benchmark_Bernstein_SmallKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + Bernstein(foo) + } +} + +func Benchmark_Murmur3___SmallKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + Murmur3(foo, M3Seed) + } +} + +func Benchmark_FNV1A_____SmallKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + FNV1A(foo) + } +} + +func Benchmark_Meiyan____SmallKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + Meiyan(foo) + } +} + +func Benchmark_Jesteress_SmallKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + Jesteress(foo) + } +} + +func Benchmark_Yorikke___SmallKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + Yorikke(foo) + } +} + +func Benchmark_Bernstein___MedKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + Bernstein(sub) + } +} + +func Benchmark_Murmur3_____MedKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + Murmur3(sub, M3Seed) + } +} + +func Benchmark_FNV1A_______MedKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + FNV1A(sub) + } +} + +func Benchmark_Meiyan______MedKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + Meiyan(sub) + } +} + +func Benchmark_Jesteress___MedKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + Jesteress(sub) + } +} + +func Benchmark_Yorikke_____MedKey(b *testing.B) { + b.SetBytes(1) + for i := 0; i < b.N; i++ { + Yorikke(sub) + } +} diff --git a/hash/results.txt b/hash/results.txt new file mode 100644 index 00000000..8b6c063e --- /dev/null +++ b/hash/results.txt @@ -0,0 +1,75 @@ +NOTE: I used SetBytes(1) to give quick estimate of ops/sec + +2012 MacbookAir 11" i7 2Ghz + +================ +OSX - Mountain Lion +Go version go1.0.3 +================ + +go test --bench="." -gcflags="-B" + +Benchmark_Bernstein_SmallKey 200000000 9.17 ns/op 109.03 MB/s +Benchmark_Murmur3___SmallKey 200000000 9.68 ns/op 103.27 MB/s +Benchmark_FNV1A_____SmallKey 200000000 9.21 ns/op 108.58 MB/s +Benchmark_Meiyan____SmallKey 500000000 6.34 ns/op 157.82 MB/s +Benchmark_Jesteress_SmallKey 500000000 6.37 ns/op 157.01 MB/s +Benchmark_Yorikke___SmallKey 500000000 6.98 ns/op 143.34 MB/s +Benchmark_Bernstein___MedKey 50000000 53.4 ns/op 18.71 MB/s +Benchmark_Murmur3_____MedKey 100000000 29.8 ns/op 33.56 MB/s +Benchmark_FNV1A_______MedKey 50000000 52.6 ns/op 19.02 MB/s +Benchmark_Meiyan______MedKey 200000000 8.84 ns/op 113.14 MB/s +Benchmark_Jesteress___MedKey 200000000 7.94 ns/op 125.90 MB/s +Benchmark_Yorikke_____MedKey 200000000 9.48 ns/op 105.50 MB/s + + +================ +Ubunutu 12.10 +Go version go1.0.3 +gcc version 4.7.2 (Ubuntu/Linaro 4.7.2-4precise1) +================ + +go test --bench="." -gcflags="-B" + +Benchmark_Bernstein_SmallKey 200000000 9.90 ns/op 101.06 MB/s +Benchmark_Murmur3___SmallKey 100000000 10.1 ns/op 98.96 MB/s +Benchmark_FNV1A_____SmallKey 200000000 9.29 ns/op 107.59 MB/s +Benchmark_Meiyan____SmallKey 500000000 6.15 ns/op 162.50 MB/s +Benchmark_Jesteress_SmallKey 500000000 6.78 ns/op 147.58 MB/s +Benchmark_Yorikke___SmallKey 500000000 7.17 ns/op 139.49 MB/s +Benchmark_Bernstein___MedKey 50000000 55.0 ns/op 18.18 MB/s +Benchmark_Murmur3_____MedKey 50000000 30.2 ns/op 33.13 MB/s +Benchmark_FNV1A_______MedKey 50000000 56.0 ns/op 17.86 MB/s +Benchmark_Meiyan______MedKey 200000000 9.14 ns/op 109.43 MB/s +Benchmark_Jesteress___MedKey 200000000 8.25 ns/op 121.24 MB/s +Benchmark_Yorikke_____MedKey 200000000 9.72 ns/op 102.91 MB/s + +go test --bench="." -compiler gccgo -gccgoflags="-O2" -gcflags="-B" + +Benchmark_Bernstein_SmallKey 500000000 4.70 ns/op 212.97 MB/s +Benchmark_Murmur3___SmallKey 200000000 8.18 ns/op 122.21 MB/s +Benchmark_FNV1A_____SmallKey 500000000 5.18 ns/op 193.17 MB/s +Benchmark_Meiyan____SmallKey 500000000 6.21 ns/op 161.13 MB/s +Benchmark_Jesteress_SmallKey 500000000 5.51 ns/op 181.38 MB/s +Benchmark_Yorikke___SmallKey 500000000 7.13 ns/op 140.19 MB/s +Benchmark_Bernstein___MedKey 100000000 27.8 ns/op 35.98 MB/s +Benchmark_Murmur3_____MedKey 100000000 19.1 ns/op 52.46 MB/s +Benchmark_FNV1A_______MedKey 50000000 34.7 ns/op 28.79 MB/s +Benchmark_Meiyan______MedKey 500000000 7.24 ns/op 138.10 MB/s +Benchmark_Jesteress___MedKey 500000000 6.58 ns/op 151.97 MB/s +Benchmark_Yorikke_____MedKey 200000000 9.08 ns/op 110.17 MB/s + +go test --bench="." -compiler gccgo -gccgoflags="-O3" -gcflags="-B" + +Benchmark_Bernstein_SmallKey 2000000000 1.80 ns/op 555.22 MB/s +Benchmark_Murmur3___SmallKey 200000000 8.07 ns/op 123.86 MB/s +Benchmark_FNV1A_____SmallKey 2000000000 1.89 ns/op 528.93 MB/s +Benchmark_Meiyan____SmallKey 500000000 6.20 ns/op 161.36 MB/s +Benchmark_Jesteress_SmallKey 500000000 5.47 ns/op 182.76 MB/s +Benchmark_Yorikke___SmallKey 500000000 7.11 ns/op 140.58 MB/s +Benchmark_Bernstein___MedKey 100000000 20.3 ns/op 49.18 MB/s +Benchmark_Murmur3_____MedKey 100000000 19.0 ns/op 52.58 MB/s +Benchmark_FNV1A_______MedKey 100000000 20.3 ns/op 49.35 MB/s +Benchmark_Meiyan______MedKey 500000000 6.99 ns/op 143.00 MB/s +Benchmark_Jesteress___MedKey 500000000 6.44 ns/op 155.23 MB/s +Benchmark_Yorikke_____MedKey 200000000 9.01 ns/op 110.98 MB/s