map里面的key的hash是怎么实现的


源码:src/runtime/map.go
golang的map是内置关键字,不管是get还是set都需要通过key的hash找到对应的存储实体。具体的hash过程如下代码:

  1. type maptype struct {
  2. typ _type
  3. key *_type
  4. elem *_type
  5. bucket *_type // internal type representing a hash bucket
  6. keysize uint8 // size of key slot
  7. elemsize uint8 // size of elem slot
  8. bucketsize uint16 // size of bucket
  9. flags uint32
  10. }
  11. //t *maptype
  12. alg := t.key.alg
  13. hash := alg.hash(key, uintptr(h.hash0))

这里其实就是拿到key对应的类型,然后获取当前key的类型的hash算法。然后调用hash函数。

hash函数的定义在这里:

  1. // typeAlg is also copied/used in reflect/type.go.
  2. // keep them in sync.
  3. type typeAlg struct {
  4. // function for hashing objects of this type
  5. // (ptr to object, seed) -> hash
  6. hash func(unsafe.Pointer, uintptr) uintptr
  7. // function for comparing objects of this type
  8. // (ptr to object A, ptr to object B) -> ==?
  9. equal func(unsafe.Pointer, unsafe.Pointer) bool
  10. }

可以知道,入参是指向key的指针,第二个参数是hash种子。

golang里面的对象的hash原理

golang里面每种数据类型的hash是与数据类型强相关的,并且是由编译器负责做类型绑定的。在src/runtime/alg.go 里面定义个一个map[type]typeAlg,用于表示每个数据类型对应的 typeAlg。

  1. var algarray = [alg_max]typeAlg{
  2. alg_NOEQ: {nil, nil},
  3. alg_MEM0: {memhash0, memequal0},
  4. alg_MEM8: {memhash8, memequal8},
  5. alg_MEM16: {memhash16, memequal16},
  6. alg_MEM32: {memhash32, memequal32},
  7. alg_MEM64: {memhash64, memequal64},
  8. alg_MEM128: {memhash128, memequal128},
  9. alg_STRING: {strhash, strequal},
  10. alg_INTER: {interhash, interequal},
  11. alg_NILINTER: {nilinterhash, nilinterequal},
  12. alg_FLOAT32: {f32hash, f32equal},
  13. alg_FLOAT64: {f64hash, f64equal},
  14. alg_CPLX64: {c64hash, c64equal},
  15. alg_CPLX128: {c128hash, c128equal},
  16. }

上面的hash函数,最后实际上底层都会调用:func memhash(p unsafe.Pointer, seed, s uintptr) uintptr 函数。以strhash函数为例:

  1. func strhash(a unsafe.Pointer, h uintptr) uintptr {
  2. x := (*stringStruct)(a)
  3. return memhash(x.str, h, uintptr(x.len))
  4. }

下面看一下memhash函数, src/runtime/hash64.go

  1. // p表示需要hash的对象的地址
  2. // seed 是hash 种子,
  3. // s是需要hash的对象的字节数
  4. func memhash(p unsafe.Pointer, seed, s uintptr) uintptr {
  5. if (GOARCH == "amd64" || GOARCH == "arm64") &&
  6. GOOS != "nacl" && useAeshash {
  7. return aeshash(p, seed, s)
  8. }
  9. h := uint64(seed + s*hashkey[0])
  10. tail:
  11. switch {
  12. case s == 0:
  13. case s < 4:
  14. h ^= uint64(*(*byte)(p))
  15. h ^= uint64(*(*byte)(add(p, s>>1))) << 8
  16. h ^= uint64(*(*byte)(add(p, s-1))) << 16
  17. h = rotl_31(h*m1) * m2
  18. case s <= 8:
  19. h ^= uint64(readUnaligned32(p))
  20. h ^= uint64(readUnaligned32(add(p, s-4))) << 32
  21. h = rotl_31(h*m1) * m2
  22. case s <= 16:
  23. h ^= readUnaligned64(p)
  24. h = rotl_31(h*m1) * m2
  25. h ^= readUnaligned64(add(p, s-8))
  26. h = rotl_31(h*m1) * m2
  27. case s <= 32:
  28. h ^= readUnaligned64(p)
  29. h = rotl_31(h*m1) * m2
  30. h ^= readUnaligned64(add(p, 8))
  31. h = rotl_31(h*m1) * m2
  32. h ^= readUnaligned64(add(p, s-16))
  33. h = rotl_31(h*m1) * m2
  34. h ^= readUnaligned64(add(p, s-8))
  35. h = rotl_31(h*m1) * m2
  36. default:
  37. v1 := h
  38. v2 := uint64(seed * hashkey[1])
  39. v3 := uint64(seed * hashkey[2])
  40. v4 := uint64(seed * hashkey[3])
  41. for s >= 32 {
  42. v1 ^= readUnaligned64(p)
  43. v1 = rotl_31(v1*m1) * m2
  44. p = add(p, 8)
  45. v2 ^= readUnaligned64(p)
  46. v2 = rotl_31(v2*m2) * m3
  47. p = add(p, 8)
  48. v3 ^= readUnaligned64(p)
  49. v3 = rotl_31(v3*m3) * m4
  50. p = add(p, 8)
  51. v4 ^= readUnaligned64(p)
  52. v4 = rotl_31(v4*m4) * m1
  53. p = add(p, 8)
  54. s -= 32
  55. }
  56. h = v1 ^ v2 ^ v3 ^ v4
  57. goto tail
  58. }
  59. h ^= h >> 29
  60. h *= m3
  61. h ^= h >> 32
  62. return uintptr(h)
  63. }


这个就是整体的hash实现。 对于amd64会使用汇编实现的aeshash函数计算hash。
具体aeshash实现,有兴趣可以看汇编:src/runtime/asm_amd64.s 里面的 runtime·aeshash 函数。

Hash 测试

下面以string作为key,来测试以下三种算法的性能:

  1. fnv算法
  2. memhash算法
  3. aeshash的汇编算法

fnv

测试背景:10000个uuid string;

  1. const LEN = 10000
  2. // the length of element is 36
  3. var keys [LEN][]byte
  4. func init(){
  5. for i:=0; i<LEN; i++ {
  6. k := uuid.New().String()
  7. keys[i] = []byte(k)
  8. }
  9. }
  10. // 777000ns
  11. func main(){
  12. start := time.Now().UnixNano()
  13. for _, k := range keys {
  14. h := fnv.New64()
  15. h.Write(k)
  16. h.Sum64()
  17. }
  18. end := time.Now().UnixNano()
  19. fmt.Println("total time:", end-start, "ns")
  20. }

执行结果:10000次hash大约是727000ns,也就是平均每次hash要花费72ns。

memhash

这个是调用golang runtime里面的内嵌hash代码,这部分是我从runtime里面copy出来的。
代码如下:

  1. package main
  2. const LEN = 10000
  3. // the length of element is 36
  4. var keys [LEN]string
  5. func init(){
  6. for i:=0; i<LEN; i++ {
  7. keys[i] = uuid.New().String()
  8. }
  9. }
  10. // 178000ns
  11. func main() {
  12. start := time.Now().UnixNano()
  13. for _, k := range keys {
  14. memhash(unsafe.Pointer(&k), 1, 36)
  15. }
  16. end := time.Now().UnixNano()
  17. fmt.Println("total time:", end-start, "ns")
  18. }
  19. // used in hash{32,64}.go to seed the hash function
  20. var hashkey [4]uintptr
  21. const PtrSize = 4 << (^uintptr(0) >> 63)
  22. func init() {
  23. for i:=0; i<4; i++ {
  24. hashkey[i]= uintptr(rand.Int63())
  25. }
  26. hashkey[0] |= 1 // make sure these numbers are odd
  27. hashkey[1] |= 1
  28. hashkey[2] |= 1
  29. hashkey[3] |= 1
  30. }
  31. func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
  32. return unsafe.Pointer(uintptr(p) + x)
  33. }
  34. func rotl_31(x uint64) uint64 {
  35. return (x << 31) | (x >> (64 - 31))
  36. }
  37. const (
  38. // Constants for multiplication: four random odd 64-bit numbers.
  39. m1 = 16877499708836156737
  40. m2 = 2820277070424839065
  41. m3 = 9497967016996688599
  42. m4 = 15839092249703872147
  43. )
  44. const (
  45. BigEndian = false
  46. DefaultPhysPageSize = 4096
  47. PCQuantum = 1
  48. Int64Align = 8
  49. MinFrameSize = 0
  50. )
  51. func readUnaligned64(p unsafe.Pointer) uint64 {
  52. q := (*[8]byte)(p)
  53. if BigEndian {
  54. return uint64(q[7]) | uint64(q[6])<<8 | uint64(q[5])<<16 | uint64(q[4])<<24 |
  55. uint64(q[3])<<32 | uint64(q[2])<<40 | uint64(q[1])<<48 | uint64(q[0])<<56
  56. }
  57. return uint64(q[0]) | uint64(q[1])<<8 | uint64(q[2])<<16 | uint64(q[3])<<24 | uint64(q[4])<<32 | uint64(q[5])<<40 | uint64(q[6])<<48 | uint64(q[7])<<56
  58. }
  59. // Note: These routines perform the read with an native endianness.
  60. func readUnaligned32(p unsafe.Pointer) uint32 {
  61. q := (*[4]byte)(p)
  62. if BigEndian {
  63. return uint32(q[3]) | uint32(q[2])<<8 | uint32(q[1])<<16 | uint32(q[0])<<24
  64. }
  65. return uint32(q[0]) | uint32(q[1])<<8 | uint32(q[2])<<16 | uint32(q[3])<<24
  66. }
  67. func memhash(p unsafe.Pointer, seed, s uintptr) uintptr {
  68. h := uint64(seed + s*hashkey[0])
  69. tail:
  70. switch {
  71. case s == 0:
  72. case s < 4:
  73. h ^= uint64(*(*byte)(p))
  74. h ^= uint64(*(*byte)(add(p, s>>1))) << 8
  75. h ^= uint64(*(*byte)(add(p, s-1))) << 16
  76. h = rotl_31(h*m1) * m2
  77. case s <= 8:
  78. h ^= uint64(readUnaligned32(p))
  79. h ^= uint64(readUnaligned32(add(p, s-4))) << 32
  80. h = rotl_31(h*m1) * m2
  81. case s <= 16:
  82. h ^= readUnaligned64(p)
  83. h = rotl_31(h*m1) * m2
  84. h ^= readUnaligned64(add(p, s-8))
  85. h = rotl_31(h*m1) * m2
  86. case s <= 32:
  87. h ^= readUnaligned64(p)
  88. h = rotl_31(h*m1) * m2
  89. h ^= readUnaligned64(add(p, 8))
  90. h = rotl_31(h*m1) * m2
  91. h ^= readUnaligned64(add(p, s-16))
  92. h = rotl_31(h*m1) * m2
  93. h ^= readUnaligned64(add(p, s-8))
  94. h = rotl_31(h*m1) * m2
  95. default:
  96. v1 := h
  97. v2 := uint64(seed * hashkey[1])
  98. v3 := uint64(seed * hashkey[2])
  99. v4 := uint64(seed * hashkey[3])
  100. for s >= 32 {
  101. v1 ^= readUnaligned64(p)
  102. v1 = rotl_31(v1*m1) * m2
  103. p = add(p, 8)
  104. v2 ^= readUnaligned64(p)
  105. v2 = rotl_31(v2*m2) * m3
  106. p = add(p, 8)
  107. v3 ^= readUnaligned64(p)
  108. v3 = rotl_31(v3*m3) * m4
  109. p = add(p, 8)
  110. v4 ^= readUnaligned64(p)
  111. v4 = rotl_31(v4*m4) * m1
  112. p = add(p, 8)
  113. s -= 32
  114. }
  115. h = v1 ^ v2 ^ v3 ^ v4
  116. goto tail
  117. }
  118. h ^= h >> 29
  119. h *= m3
  120. h ^= h >> 32
  121. return uintptr(h)
  122. }


执行结果:10000次hash大约是178000ns,也就是平均每次hash要花费17.8ns。

aeshash汇编

这部分代码也是从runtime asm代码copy出来做测试:
注意,需要把src/runtime/go_tls.h、funcdata.h、textflag.h 这个三个头文件放在当前目录下。

  1. // Copyright 2009 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. #include "go_asm.h"
  5. #include "go_tls.h"
  6. #include "funcdata.h"
  7. #include "textflag.h"
  8. //louyuting
  9. // func aeshashstr(p unsafe.Pointer, h uintptr) uintptr
  10. TEXT ·aeshashstr2(SB),NOSPLIT,$0-24
  11. MOVQ p+0(FP), AX // ptr to string struct
  12. MOVQ 8(AX), CX // length of string
  13. MOVQ (AX), AX // string data
  14. LEAQ ret+16(FP), DX
  15. JMP aeshashbody<>(SB)
  16. // AX: data
  17. // CX: length
  18. // DX: address to put return value
  19. TEXT aeshashbody<>(SB),NOSPLIT,$0-0
  20. // Fill an SSE register with our seeds.
  21. MOVQ h+8(FP), X0 // 64 bits of per-table hash seed
  22. PINSRW $4, CX, X0 // 16 bits of length
  23. PSHUFHW $0, X0, X0 // repeat length 4 times total
  24. MOVO X0, X1 // save unscrambled seed
  25. PXOR main·aeskeysched(SB), X0 // xor in per-process seed
  26. AESENC X0, X0 // scramble seed
  27. CMPQ CX, $16
  28. JB aes0to15
  29. JE aes16
  30. CMPQ CX, $32
  31. JBE aes17to32
  32. CMPQ CX, $64
  33. JBE aes33to64
  34. CMPQ CX, $128
  35. JBE aes65to128
  36. JMP aes129plus
  37. aes0to15:
  38. TESTQ CX, CX
  39. JE aes0
  40. ADDQ $16, AX
  41. TESTW $0xff0, AX
  42. JE endofpage
  43. // 16 bytes loaded at this address won't cross
  44. // a page boundary, so we can load it directly.
  45. MOVOU -16(AX), X1
  46. ADDQ CX, CX
  47. MOVQ $masks<>(SB), AX
  48. PAND (AX)(CX*8), X1
  49. final1:
  50. PXOR X0, X1 // xor data with seed
  51. AESENC X1, X1 // scramble combo 3 times
  52. AESENC X1, X1
  53. AESENC X1, X1
  54. MOVQ X1, (DX)
  55. RET
  56. endofpage:
  57. // address ends in 1111xxxx. Might be up against
  58. // a page boundary, so load ending at last byte.
  59. // Then shift bytes down using pshufb.
  60. MOVOU -32(AX)(CX*1), X1
  61. ADDQ CX, CX
  62. MOVQ $shifts<>(SB), AX
  63. PSHUFB (AX)(CX*8), X1
  64. JMP final1
  65. aes0:
  66. // Return scrambled input seed
  67. AESENC X0, X0
  68. MOVQ X0, (DX)
  69. RET
  70. aes16:
  71. MOVOU (AX), X1
  72. JMP final1
  73. aes17to32:
  74. // make second starting seed
  75. PXOR main·aeskeysched+16(SB), X1
  76. AESENC X1, X1
  77. // load data to be hashed
  78. MOVOU (AX), X2
  79. MOVOU -16(AX)(CX*1), X3
  80. // xor with seed
  81. PXOR X0, X2
  82. PXOR X1, X3
  83. // scramble 3 times
  84. AESENC X2, X2
  85. AESENC X3, X3
  86. AESENC X2, X2
  87. AESENC X3, X3
  88. AESENC X2, X2
  89. AESENC X3, X3
  90. // combine results
  91. PXOR X3, X2
  92. MOVQ X2, (DX)
  93. RET
  94. aes33to64:
  95. // make 3 more starting seeds
  96. MOVO X1, X2
  97. MOVO X1, X3
  98. PXOR main·aeskeysched+16(SB), X1
  99. PXOR main·aeskeysched+32(SB), X2
  100. PXOR main·aeskeysched+48(SB), X3
  101. AESENC X1, X1
  102. AESENC X2, X2
  103. AESENC X3, X3
  104. MOVOU (AX), X4
  105. MOVOU 16(AX), X5
  106. MOVOU -32(AX)(CX*1), X6
  107. MOVOU -16(AX)(CX*1), X7
  108. PXOR X0, X4
  109. PXOR X1, X5
  110. PXOR X2, X6
  111. PXOR X3, X7
  112. AESENC X4, X4
  113. AESENC X5, X5
  114. AESENC X6, X6
  115. AESENC X7, X7
  116. AESENC X4, X4
  117. AESENC X5, X5
  118. AESENC X6, X6
  119. AESENC X7, X7
  120. AESENC X4, X4
  121. AESENC X5, X5
  122. AESENC X6, X6
  123. AESENC X7, X7
  124. PXOR X6, X4
  125. PXOR X7, X5
  126. PXOR X5, X4
  127. MOVQ X4, (DX)
  128. RET
  129. aes65to128:
  130. // make 7 more starting seeds
  131. MOVO X1, X2
  132. MOVO X1, X3
  133. MOVO X1, X4
  134. MOVO X1, X5
  135. MOVO X1, X6
  136. MOVO X1, X7
  137. PXOR main·aeskeysched+16(SB), X1
  138. PXOR main·aeskeysched+32(SB), X2
  139. PXOR main·aeskeysched+48(SB), X3
  140. PXOR main·aeskeysched+64(SB), X4
  141. PXOR main·aeskeysched+80(SB), X5
  142. PXOR main·aeskeysched+96(SB), X6
  143. PXOR main·aeskeysched+112(SB), X7
  144. AESENC X1, X1
  145. AESENC X2, X2
  146. AESENC X3, X3
  147. AESENC X4, X4
  148. AESENC X5, X5
  149. AESENC X6, X6
  150. AESENC X7, X7
  151. // load data
  152. MOVOU (AX), X8
  153. MOVOU 16(AX), X9
  154. MOVOU 32(AX), X10
  155. MOVOU 48(AX), X11
  156. MOVOU -64(AX)(CX*1), X12
  157. MOVOU -48(AX)(CX*1), X13
  158. MOVOU -32(AX)(CX*1), X14
  159. MOVOU -16(AX)(CX*1), X15
  160. // xor with seed
  161. PXOR X0, X8
  162. PXOR X1, X9
  163. PXOR X2, X10
  164. PXOR X3, X11
  165. PXOR X4, X12
  166. PXOR X5, X13
  167. PXOR X6, X14
  168. PXOR X7, X15
  169. // scramble 3 times
  170. AESENC X8, X8
  171. AESENC X9, X9
  172. AESENC X10, X10
  173. AESENC X11, X11
  174. AESENC X12, X12
  175. AESENC X13, X13
  176. AESENC X14, X14
  177. AESENC X15, X15
  178. AESENC X8, X8
  179. AESENC X9, X9
  180. AESENC X10, X10
  181. AESENC X11, X11
  182. AESENC X12, X12
  183. AESENC X13, X13
  184. AESENC X14, X14
  185. AESENC X15, X15
  186. AESENC X8, X8
  187. AESENC X9, X9
  188. AESENC X10, X10
  189. AESENC X11, X11
  190. AESENC X12, X12
  191. AESENC X13, X13
  192. AESENC X14, X14
  193. AESENC X15, X15
  194. // combine results
  195. PXOR X12, X8
  196. PXOR X13, X9
  197. PXOR X14, X10
  198. PXOR X15, X11
  199. PXOR X10, X8
  200. PXOR X11, X9
  201. PXOR X9, X8
  202. MOVQ X8, (DX)
  203. RET
  204. aes129plus:
  205. // make 7 more starting seeds
  206. MOVO X1, X2
  207. MOVO X1, X3
  208. MOVO X1, X4
  209. MOVO X1, X5
  210. MOVO X1, X6
  211. MOVO X1, X7
  212. PXOR main·aeskeysched+16(SB), X1
  213. PXOR main·aeskeysched+32(SB), X2
  214. PXOR main·aeskeysched+48(SB), X3
  215. PXOR main·aeskeysched+64(SB), X4
  216. PXOR main·aeskeysched+80(SB), X5
  217. PXOR main·aeskeysched+96(SB), X6
  218. PXOR main·aeskeysched+112(SB), X7
  219. AESENC X1, X1
  220. AESENC X2, X2
  221. AESENC X3, X3
  222. AESENC X4, X4
  223. AESENC X5, X5
  224. AESENC X6, X6
  225. AESENC X7, X7
  226. // start with last (possibly overlapping) block
  227. MOVOU -128(AX)(CX*1), X8
  228. MOVOU -112(AX)(CX*1), X9
  229. MOVOU -96(AX)(CX*1), X10
  230. MOVOU -80(AX)(CX*1), X11
  231. MOVOU -64(AX)(CX*1), X12
  232. MOVOU -48(AX)(CX*1), X13
  233. MOVOU -32(AX)(CX*1), X14
  234. MOVOU -16(AX)(CX*1), X15
  235. // xor in seed
  236. PXOR X0, X8
  237. PXOR X1, X9
  238. PXOR X2, X10
  239. PXOR X3, X11
  240. PXOR X4, X12
  241. PXOR X5, X13
  242. PXOR X6, X14
  243. PXOR X7, X15
  244. // compute number of remaining 128-byte blocks
  245. DECQ CX
  246. SHRQ $7, CX
  247. aesloop:
  248. // scramble state
  249. AESENC X8, X8
  250. AESENC X9, X9
  251. AESENC X10, X10
  252. AESENC X11, X11
  253. AESENC X12, X12
  254. AESENC X13, X13
  255. AESENC X14, X14
  256. AESENC X15, X15
  257. // scramble state, xor in a block
  258. MOVOU (AX), X0
  259. MOVOU 16(AX), X1
  260. MOVOU 32(AX), X2
  261. MOVOU 48(AX), X3
  262. AESENC X0, X8
  263. AESENC X1, X9
  264. AESENC X2, X10
  265. AESENC X3, X11
  266. MOVOU 64(AX), X4
  267. MOVOU 80(AX), X5
  268. MOVOU 96(AX), X6
  269. MOVOU 112(AX), X7
  270. AESENC X4, X12
  271. AESENC X5, X13
  272. AESENC X6, X14
  273. AESENC X7, X15
  274. ADDQ $128, AX
  275. DECQ CX
  276. JNE aesloop
  277. // 3 more scrambles to finish
  278. AESENC X8, X8
  279. AESENC X9, X9
  280. AESENC X10, X10
  281. AESENC X11, X11
  282. AESENC X12, X12
  283. AESENC X13, X13
  284. AESENC X14, X14
  285. AESENC X15, X15
  286. AESENC X8, X8
  287. AESENC X9, X9
  288. AESENC X10, X10
  289. AESENC X11, X11
  290. AESENC X12, X12
  291. AESENC X13, X13
  292. AESENC X14, X14
  293. AESENC X15, X15
  294. AESENC X8, X8
  295. AESENC X9, X9
  296. AESENC X10, X10
  297. AESENC X11, X11
  298. AESENC X12, X12
  299. AESENC X13, X13
  300. AESENC X14, X14
  301. AESENC X15, X15
  302. PXOR X12, X8
  303. PXOR X13, X9
  304. PXOR X14, X10
  305. PXOR X15, X11
  306. PXOR X10, X8
  307. PXOR X11, X9
  308. PXOR X9, X8
  309. MOVQ X8, (DX)
  310. RET
  311. // simple mask to get rid of data in the high part of the register.
  312. DATA masks<>+0x00(SB)/8, $0x0000000000000000
  313. DATA masks<>+0x08(SB)/8, $0x0000000000000000
  314. DATA masks<>+0x10(SB)/8, $0x00000000000000ff
  315. DATA masks<>+0x18(SB)/8, $0x0000000000000000
  316. DATA masks<>+0x20(SB)/8, $0x000000000000ffff
  317. DATA masks<>+0x28(SB)/8, $0x0000000000000000
  318. DATA masks<>+0x30(SB)/8, $0x0000000000ffffff
  319. DATA masks<>+0x38(SB)/8, $0x0000000000000000
  320. DATA masks<>+0x40(SB)/8, $0x00000000ffffffff
  321. DATA masks<>+0x48(SB)/8, $0x0000000000000000
  322. DATA masks<>+0x50(SB)/8, $0x000000ffffffffff
  323. DATA masks<>+0x58(SB)/8, $0x0000000000000000
  324. DATA masks<>+0x60(SB)/8, $0x0000ffffffffffff
  325. DATA masks<>+0x68(SB)/8, $0x0000000000000000
  326. DATA masks<>+0x70(SB)/8, $0x00ffffffffffffff
  327. DATA masks<>+0x78(SB)/8, $0x0000000000000000
  328. DATA masks<>+0x80(SB)/8, $0xffffffffffffffff
  329. DATA masks<>+0x88(SB)/8, $0x0000000000000000
  330. DATA masks<>+0x90(SB)/8, $0xffffffffffffffff
  331. DATA masks<>+0x98(SB)/8, $0x00000000000000ff
  332. DATA masks<>+0xa0(SB)/8, $0xffffffffffffffff
  333. DATA masks<>+0xa8(SB)/8, $0x000000000000ffff
  334. DATA masks<>+0xb0(SB)/8, $0xffffffffffffffff
  335. DATA masks<>+0xb8(SB)/8, $0x0000000000ffffff
  336. DATA masks<>+0xc0(SB)/8, $0xffffffffffffffff
  337. DATA masks<>+0xc8(SB)/8, $0x00000000ffffffff
  338. DATA masks<>+0xd0(SB)/8, $0xffffffffffffffff
  339. DATA masks<>+0xd8(SB)/8, $0x000000ffffffffff
  340. DATA masks<>+0xe0(SB)/8, $0xffffffffffffffff
  341. DATA masks<>+0xe8(SB)/8, $0x0000ffffffffffff
  342. DATA masks<>+0xf0(SB)/8, $0xffffffffffffffff
  343. DATA masks<>+0xf8(SB)/8, $0x00ffffffffffffff
  344. GLOBL masks<>(SB),RODATA,$256
  345. // func checkASM() bool
  346. TEXT ·checkASM(SB),NOSPLIT,$0-1
  347. // check that masks<>(SB) and shifts<>(SB) are aligned to 16-byte
  348. MOVQ $masks<>(SB), AX
  349. MOVQ $shifts<>(SB), BX
  350. ORQ BX, AX
  351. TESTQ $15, AX
  352. SETEQ ret+0(FP)
  353. RET
  354. // these are arguments to pshufb. They move data down from
  355. // the high bytes of the register to the low bytes of the register.
  356. // index is how many bytes to move.
  357. DATA shifts<>+0x00(SB)/8, $0x0000000000000000
  358. DATA shifts<>+0x08(SB)/8, $0x0000000000000000
  359. DATA shifts<>+0x10(SB)/8, $0xffffffffffffff0f
  360. DATA shifts<>+0x18(SB)/8, $0xffffffffffffffff
  361. DATA shifts<>+0x20(SB)/8, $0xffffffffffff0f0e
  362. DATA shifts<>+0x28(SB)/8, $0xffffffffffffffff
  363. DATA shifts<>+0x30(SB)/8, $0xffffffffff0f0e0d
  364. DATA shifts<>+0x38(SB)/8, $0xffffffffffffffff
  365. DATA shifts<>+0x40(SB)/8, $0xffffffff0f0e0d0c
  366. DATA shifts<>+0x48(SB)/8, $0xffffffffffffffff
  367. DATA shifts<>+0x50(SB)/8, $0xffffff0f0e0d0c0b
  368. DATA shifts<>+0x58(SB)/8, $0xffffffffffffffff
  369. DATA shifts<>+0x60(SB)/8, $0xffff0f0e0d0c0b0a
  370. DATA shifts<>+0x68(SB)/8, $0xffffffffffffffff
  371. DATA shifts<>+0x70(SB)/8, $0xff0f0e0d0c0b0a09
  372. DATA shifts<>+0x78(SB)/8, $0xffffffffffffffff
  373. DATA shifts<>+0x80(SB)/8, $0x0f0e0d0c0b0a0908
  374. DATA shifts<>+0x88(SB)/8, $0xffffffffffffffff
  375. DATA shifts<>+0x90(SB)/8, $0x0e0d0c0b0a090807
  376. DATA shifts<>+0x98(SB)/8, $0xffffffffffffff0f
  377. DATA shifts<>+0xa0(SB)/8, $0x0d0c0b0a09080706
  378. DATA shifts<>+0xa8(SB)/8, $0xffffffffffff0f0e
  379. DATA shifts<>+0xb0(SB)/8, $0x0c0b0a0908070605
  380. DATA shifts<>+0xb8(SB)/8, $0xffffffffff0f0e0d
  381. DATA shifts<>+0xc0(SB)/8, $0x0b0a090807060504
  382. DATA shifts<>+0xc8(SB)/8, $0xffffffff0f0e0d0c
  383. DATA shifts<>+0xd0(SB)/8, $0x0a09080706050403
  384. DATA shifts<>+0xd8(SB)/8, $0xffffff0f0e0d0c0b
  385. DATA shifts<>+0xe0(SB)/8, $0x0908070605040302
  386. DATA shifts<>+0xe8(SB)/8, $0xffff0f0e0d0c0b0a
  387. DATA shifts<>+0xf0(SB)/8, $0x0807060504030201
  388. DATA shifts<>+0xf8(SB)/8, $0xff0f0e0d0c0b0a09
  389. GLOBL shifts<>(SB),RODATA,$256

测试结果来看,10000个记录,大概也是182000ns, 每次大概是18.2ns。

总结:

算法 per hash cost
fnv 72ns
memhash 17.8ns
aeshash 汇编 18.2ns

从测试来看,memhash的hash和aeshash性能差不多,但是比fnv就要好很多了。

转自:https://blog.csdn.net/u010853261/article/details/104148615