]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: using wyhash for memhashFallback on 32bit platform
authorMeng Zhuo <mzh@golangcn.org>
Mon, 15 Mar 2021 11:48:39 +0000 (19:48 +0800)
committerMeng Zhuo <mzh@golangcn.org>
Tue, 16 Mar 2021 00:50:54 +0000 (00:50 +0000)
wyhash is a general hash function that:

1. Default hash function of Zig, Nim
2. Passed Smhasher, BigCrush and PractRand
3. Less code
4. 3~26% faster than internal hashmap

name                  old time/op    new time/op    delta
Hash5                   67.8ns ± 0%    65.4ns ± 0%   -3.45%  (p=0.000 n=7+10)
Hash16                  82.5ns ± 0%    74.2ns ± 0%  -10.12%  (p=0.000 n=6+8)
Hash64                   121ns ± 0%     102ns ± 0%  -15.82%  (p=0.000 n=7+10)
Hash1024                1.13µs ± 0%    0.89µs ± 0%  -20.58%  (p=0.000 n=10+9)
Hash65536               68.9µs ± 0%    54.4µs ± 0%  -21.04%  (p=0.000 n=10+7)
HashStringSpeed          103ns ± 2%      93ns ± 3%  -10.24%  (p=0.000 n=9+10)
HashBytesSpeed           191ns ± 2%     180ns ± 1%   -5.40%  (p=0.000 n=10+8)
HashInt32Speed          59.0ns ± 2%    59.1ns ± 1%     ~     (p=0.655 n=9+8)
HashInt64Speed          72.7ns ± 3%    66.1ns ± 5%   -9.04%  (p=0.000 n=10+10)
HashStringArraySpeed     270ns ± 1%     222ns ± 2%  -17.91%  (p=0.000 n=10+10)
FastrandHashiter         108ns ± 0%     109ns ± 1%   +0.96%  (p=0.002 n=10+10)

name                  old speed      new speed      delta
Hash5                 73.8MB/s ± 0%  76.4MB/s ± 0%   +3.58%  (p=0.000 n=7+10)
Hash16                 194MB/s ± 0%   216MB/s ± 0%  +11.25%  (p=0.000 n=10+8)
Hash64                 530MB/s ± 0%   630MB/s ± 0%  +18.74%  (p=0.000 n=8+10)
Hash1024               910MB/s ± 0%  1145MB/s ± 0%  +25.88%  (p=0.000 n=10+9)
Hash65536              951MB/s ± 0%  1204MB/s ± 0%  +26.64%  (p=0.000 n=10+7)

Update #43130

Change-Id: Id00c54b116a2411fcf675e95896fffb85f0e25b6
Reviewed-on: https://go-review.googlesource.com/c/go/+/280372
Trust: Meng Zhuo <mzh@golangcn.org>
Run-TryBot: Meng Zhuo <mzh@golangcn.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/runtime/hash32.go

index 7fa8eb7cab1a265fec99eef967c91520bce4cab5..7c22c76b875c37c630a1042c2b3dbf1e90b89059 100644 (file)
@@ -3,8 +3,7 @@
 // license that can be found in the LICENSE file.
 
 // Hashing algorithm inspired by
-//   xxhash: https://code.google.com/p/xxhash/
-// cityhash: https://code.google.com/p/cityhash/
+// wyhash: https://github.com/wangyi-fudan/wyhash/blob/ceb019b530e2c1c14d70b79bfa2bc49de7d95bc1/Modern%20Non-Cryptographic%20Hash%20Function%20and%20Pseudorandom%20Number%20Generator.pdf
 
 //go:build 386 || arm || mips || mipsle
 // +build 386 arm mips mipsle
@@ -13,101 +12,52 @@ package runtime
 
 import "unsafe"
 
-const (
-       // Constants for multiplication: four random odd 32-bit numbers.
-       m1 = 3168982561
-       m2 = 3339683297
-       m3 = 832293441
-       m4 = 2336365089
-)
-
-func memhashFallback(p unsafe.Pointer, seed, s uintptr) uintptr {
-       h := uint32(seed + s*hashkey[0])
-tail:
-       switch {
-       case s == 0:
-       case s < 4:
-               h ^= uint32(*(*byte)(p))
-               h ^= uint32(*(*byte)(add(p, s>>1))) << 8
-               h ^= uint32(*(*byte)(add(p, s-1))) << 16
-               h = rotl_15(h*m1) * m2
-       case s == 4:
-               h ^= readUnaligned32(p)
-               h = rotl_15(h*m1) * m2
-       case s <= 8:
-               h ^= readUnaligned32(p)
-               h = rotl_15(h*m1) * m2
-               h ^= readUnaligned32(add(p, s-4))
-               h = rotl_15(h*m1) * m2
-       case s <= 16:
-               h ^= readUnaligned32(p)
-               h = rotl_15(h*m1) * m2
-               h ^= readUnaligned32(add(p, 4))
-               h = rotl_15(h*m1) * m2
-               h ^= readUnaligned32(add(p, s-8))
-               h = rotl_15(h*m1) * m2
-               h ^= readUnaligned32(add(p, s-4))
-               h = rotl_15(h*m1) * m2
-       default:
-               v1 := h
-               v2 := uint32(seed * hashkey[1])
-               v3 := uint32(seed * hashkey[2])
-               v4 := uint32(seed * hashkey[3])
-               for s >= 16 {
-                       v1 ^= readUnaligned32(p)
-                       v1 = rotl_15(v1*m1) * m2
-                       p = add(p, 4)
-                       v2 ^= readUnaligned32(p)
-                       v2 = rotl_15(v2*m2) * m3
-                       p = add(p, 4)
-                       v3 ^= readUnaligned32(p)
-                       v3 = rotl_15(v3*m3) * m4
-                       p = add(p, 4)
-                       v4 ^= readUnaligned32(p)
-                       v4 = rotl_15(v4*m4) * m1
-                       p = add(p, 4)
-                       s -= 16
-               }
-               h = v1 ^ v2 ^ v3 ^ v4
-               goto tail
-       }
-       h ^= h >> 17
-       h *= m3
-       h ^= h >> 13
-       h *= m4
-       h ^= h >> 16
-       return uintptr(h)
-}
-
 func memhash32Fallback(p unsafe.Pointer, seed uintptr) uintptr {
-       h := uint32(seed + 4*hashkey[0])
-       h ^= readUnaligned32(p)
-       h = rotl_15(h*m1) * m2
-       h ^= h >> 17
-       h *= m3
-       h ^= h >> 13
-       h *= m4
-       h ^= h >> 16
-       return uintptr(h)
+       a, b := mix32(uint32(seed), uint32(4^hashkey[0]))
+       t := readUnaligned32(p)
+       a ^= t
+       b ^= t
+       a, b = mix32(a, b)
+       a, b = mix32(a, b)
+       return uintptr(a ^ b)
 }
 
 func memhash64Fallback(p unsafe.Pointer, seed uintptr) uintptr {
-       h := uint32(seed + 8*hashkey[0])
-       h ^= readUnaligned32(p)
-       h = rotl_15(h*m1) * m2
-       h ^= readUnaligned32(add(p, 4))
-       h = rotl_15(h*m1) * m2
-       h ^= h >> 17
-       h *= m3
-       h ^= h >> 13
-       h *= m4
-       h ^= h >> 16
-       return uintptr(h)
+       a, b := mix32(uint32(seed), uint32(8^hashkey[0]))
+       a ^= readUnaligned32(p)
+       b ^= readUnaligned32(add(p, 4))
+       a, b = mix32(a, b)
+       a, b = mix32(a, b)
+       return uintptr(a ^ b)
+}
+
+func memhashFallback(p unsafe.Pointer, seed, s uintptr) uintptr {
+
+       a, b := mix32(uint32(seed), uint32(s^hashkey[0]))
+       if s == 0 {
+               return uintptr(a ^ b)
+       }
+       for ; s > 8; s -= 8 {
+               a ^= readUnaligned32(p)
+               b ^= readUnaligned32(add(p, 4))
+               a, b = mix32(a, b)
+               p = add(p, 8)
+       }
+       if s >= 4 {
+               a ^= readUnaligned32(p)
+               b ^= readUnaligned32(add(p, s-4))
+       } else {
+               t := uint32(*(*byte)(p))
+               t |= uint32(*(*byte)(add(p, s>>1))) << 8
+               t |= uint32(*(*byte)(add(p, s-1))) << 16
+               b ^= t
+       }
+       a, b = mix32(a, b)
+       a, b = mix32(a, b)
+       return uintptr(a ^ b)
 }
 
-// Note: in order to get the compiler to issue rotl instructions, we
-// need to constant fold the shift amount by hand.
-// TODO: convince the compiler to issue rotl instructions after inlining.
-func rotl_15(x uint32) uint32 {
-       return (x << 15) | (x >> (32 - 15))
+func mix32(a, b uint32) (uint32, uint32) {
+       c := uint64(a^uint32(hashkey[1])) * uint64(b^uint32(hashkey[2]))
+       return uint32(c), uint32(c >> 32)
 }