Source file
src/runtime/hash_test.go
Documentation: runtime
1
2
3
4
5 package runtime_test
6
7 import (
8 "fmt"
9 "internal/race"
10 "math"
11 "math/rand"
12 . "runtime"
13 "strings"
14 "testing"
15 "unsafe"
16 )
17
18 func TestMemHash32Equality(t *testing.T) {
19 if *UseAeshash {
20 t.Skip("skipping since AES hash implementation is used")
21 }
22 var b [4]byte
23 r := rand.New(rand.NewSource(1234))
24 seed := uintptr(r.Uint64())
25 for i := 0; i < 100; i++ {
26 randBytes(r, b[:])
27 got := MemHash32(unsafe.Pointer(&b), seed)
28 want := MemHash(unsafe.Pointer(&b), seed, 4)
29 if got != want {
30 t.Errorf("MemHash32(%x, %v) = %v; want %v", b, seed, got, want)
31 }
32 }
33 }
34
35 func TestMemHash64Equality(t *testing.T) {
36 if *UseAeshash {
37 t.Skip("skipping since AES hash implementation is used")
38 }
39 var b [8]byte
40 r := rand.New(rand.NewSource(1234))
41 seed := uintptr(r.Uint64())
42 for i := 0; i < 100; i++ {
43 randBytes(r, b[:])
44 got := MemHash64(unsafe.Pointer(&b), seed)
45 want := MemHash(unsafe.Pointer(&b), seed, 8)
46 if got != want {
47 t.Errorf("MemHash64(%x, %v) = %v; want %v", b, seed, got, want)
48 }
49 }
50 }
51
52
53
54
55
56
57
58
59
60
61
62
63 func TestSmhasherSanity(t *testing.T) {
64 r := rand.New(rand.NewSource(1234))
65 const REP = 10
66 const KEYMAX = 128
67 const PAD = 16
68 const OFFMAX = 16
69 for k := 0; k < REP; k++ {
70 for n := 0; n < KEYMAX; n++ {
71 for i := 0; i < OFFMAX; i++ {
72 var b [KEYMAX + OFFMAX + 2*PAD]byte
73 var c [KEYMAX + OFFMAX + 2*PAD]byte
74 randBytes(r, b[:])
75 randBytes(r, c[:])
76 copy(c[PAD+i:PAD+i+n], b[PAD:PAD+n])
77 if BytesHash(b[PAD:PAD+n], 0) != BytesHash(c[PAD+i:PAD+i+n], 0) {
78 t.Errorf("hash depends on bytes outside key")
79 }
80 }
81 }
82 }
83 }
84
85 type HashSet struct {
86 m map[uintptr]struct{}
87 n int
88 }
89
90 func newHashSet() *HashSet {
91 return &HashSet{make(map[uintptr]struct{}), 0}
92 }
93 func (s *HashSet) add(h uintptr) {
94 s.m[h] = struct{}{}
95 s.n++
96 }
97 func (s *HashSet) addS(x string) {
98 s.add(StringHash(x, 0))
99 }
100 func (s *HashSet) addB(x []byte) {
101 s.add(BytesHash(x, 0))
102 }
103 func (s *HashSet) addS_seed(x string, seed uintptr) {
104 s.add(StringHash(x, seed))
105 }
106 func (s *HashSet) check(t *testing.T) {
107 const SLOP = 50.0
108 collisions := s.n - len(s.m)
109 pairs := int64(s.n) * int64(s.n-1) / 2
110 expected := float64(pairs) / math.Pow(2.0, float64(hashSize))
111 stddev := math.Sqrt(expected)
112 if float64(collisions) > expected+SLOP*(3*stddev+1) {
113 t.Errorf("unexpected number of collisions: got=%d mean=%f stddev=%f threshold=%f", collisions, expected, stddev, expected+SLOP*(3*stddev+1))
114 }
115 }
116
117
118 func TestSmhasherAppendedZeros(t *testing.T) {
119 s := "hello" + strings.Repeat("\x00", 256)
120 h := newHashSet()
121 for i := 0; i <= len(s); i++ {
122 h.addS(s[:i])
123 }
124 h.check(t)
125 }
126
127
128 func TestSmhasherSmallKeys(t *testing.T) {
129 if race.Enabled {
130 t.Skip("Too long for race mode")
131 }
132 h := newHashSet()
133 var b [3]byte
134 for i := 0; i < 256; i++ {
135 b[0] = byte(i)
136 h.addB(b[:1])
137 for j := 0; j < 256; j++ {
138 b[1] = byte(j)
139 h.addB(b[:2])
140 if !testing.Short() {
141 for k := 0; k < 256; k++ {
142 b[2] = byte(k)
143 h.addB(b[:3])
144 }
145 }
146 }
147 }
148 h.check(t)
149 }
150
151
152 func TestSmhasherZeros(t *testing.T) {
153 N := 256 * 1024
154 if testing.Short() {
155 N = 1024
156 }
157 h := newHashSet()
158 b := make([]byte, N)
159 for i := 0; i <= N; i++ {
160 h.addB(b[:i])
161 }
162 h.check(t)
163 }
164
165
166 func TestSmhasherTwoNonzero(t *testing.T) {
167 if GOARCH == "wasm" {
168 t.Skip("Too slow on wasm")
169 }
170 if testing.Short() {
171 t.Skip("Skipping in short mode")
172 }
173 if race.Enabled {
174 t.Skip("Too long for race mode")
175 }
176 h := newHashSet()
177 for n := 2; n <= 16; n++ {
178 twoNonZero(h, n)
179 }
180 h.check(t)
181 }
182 func twoNonZero(h *HashSet, n int) {
183 b := make([]byte, n)
184
185
186 h.addB(b)
187
188
189 for i := 0; i < n; i++ {
190 for x := 1; x < 256; x++ {
191 b[i] = byte(x)
192 h.addB(b)
193 b[i] = 0
194 }
195 }
196
197
198 for i := 0; i < n; i++ {
199 for x := 1; x < 256; x++ {
200 b[i] = byte(x)
201 for j := i + 1; j < n; j++ {
202 for y := 1; y < 256; y++ {
203 b[j] = byte(y)
204 h.addB(b)
205 b[j] = 0
206 }
207 }
208 b[i] = 0
209 }
210 }
211 }
212
213
214 func TestSmhasherCyclic(t *testing.T) {
215 if testing.Short() {
216 t.Skip("Skipping in short mode")
217 }
218 if race.Enabled {
219 t.Skip("Too long for race mode")
220 }
221 r := rand.New(rand.NewSource(1234))
222 const REPEAT = 8
223 const N = 1000000
224 for n := 4; n <= 12; n++ {
225 h := newHashSet()
226 b := make([]byte, REPEAT*n)
227 for i := 0; i < N; i++ {
228 b[0] = byte(i * 79 % 97)
229 b[1] = byte(i * 43 % 137)
230 b[2] = byte(i * 151 % 197)
231 b[3] = byte(i * 199 % 251)
232 randBytes(r, b[4:n])
233 for j := n; j < n*REPEAT; j++ {
234 b[j] = b[j-n]
235 }
236 h.addB(b)
237 }
238 h.check(t)
239 }
240 }
241
242
243 func TestSmhasherSparse(t *testing.T) {
244 if GOARCH == "wasm" {
245 t.Skip("Too slow on wasm")
246 }
247 if testing.Short() {
248 t.Skip("Skipping in short mode")
249 }
250 sparse(t, 32, 6)
251 sparse(t, 40, 6)
252 sparse(t, 48, 5)
253 sparse(t, 56, 5)
254 sparse(t, 64, 5)
255 sparse(t, 96, 4)
256 sparse(t, 256, 3)
257 sparse(t, 2048, 2)
258 }
259 func sparse(t *testing.T, n int, k int) {
260 b := make([]byte, n/8)
261 h := newHashSet()
262 setbits(h, b, 0, k)
263 h.check(t)
264 }
265
266
267 func setbits(h *HashSet, b []byte, i int, k int) {
268 h.addB(b)
269 if k == 0 {
270 return
271 }
272 for j := i; j < len(b)*8; j++ {
273 b[j/8] |= byte(1 << uint(j&7))
274 setbits(h, b, j+1, k-1)
275 b[j/8] &= byte(^(1 << uint(j&7)))
276 }
277 }
278
279
280
281 func TestSmhasherPermutation(t *testing.T) {
282 if GOARCH == "wasm" {
283 t.Skip("Too slow on wasm")
284 }
285 if testing.Short() {
286 t.Skip("Skipping in short mode")
287 }
288 if race.Enabled {
289 t.Skip("Too long for race mode")
290 }
291 permutation(t, []uint32{0, 1, 2, 3, 4, 5, 6, 7}, 8)
292 permutation(t, []uint32{0, 1 << 29, 2 << 29, 3 << 29, 4 << 29, 5 << 29, 6 << 29, 7 << 29}, 8)
293 permutation(t, []uint32{0, 1}, 20)
294 permutation(t, []uint32{0, 1 << 31}, 20)
295 permutation(t, []uint32{0, 1, 2, 3, 4, 5, 6, 7, 1 << 29, 2 << 29, 3 << 29, 4 << 29, 5 << 29, 6 << 29, 7 << 29}, 6)
296 }
297 func permutation(t *testing.T, s []uint32, n int) {
298 b := make([]byte, n*4)
299 h := newHashSet()
300 genPerm(h, b, s, 0)
301 h.check(t)
302 }
303 func genPerm(h *HashSet, b []byte, s []uint32, n int) {
304 h.addB(b[:n])
305 if n == len(b) {
306 return
307 }
308 for _, v := range s {
309 b[n] = byte(v)
310 b[n+1] = byte(v >> 8)
311 b[n+2] = byte(v >> 16)
312 b[n+3] = byte(v >> 24)
313 genPerm(h, b, s, n+4)
314 }
315 }
316
317 type Key interface {
318 clear()
319 random(r *rand.Rand)
320 bits() int
321 flipBit(i int)
322 hash() uintptr
323 name() string
324 }
325
326 type BytesKey struct {
327 b []byte
328 }
329
330 func (k *BytesKey) clear() {
331 for i := range k.b {
332 k.b[i] = 0
333 }
334 }
335 func (k *BytesKey) random(r *rand.Rand) {
336 randBytes(r, k.b)
337 }
338 func (k *BytesKey) bits() int {
339 return len(k.b) * 8
340 }
341 func (k *BytesKey) flipBit(i int) {
342 k.b[i>>3] ^= byte(1 << uint(i&7))
343 }
344 func (k *BytesKey) hash() uintptr {
345 return BytesHash(k.b, 0)
346 }
347 func (k *BytesKey) name() string {
348 return fmt.Sprintf("bytes%d", len(k.b))
349 }
350
351 type Int32Key struct {
352 i uint32
353 }
354
355 func (k *Int32Key) clear() {
356 k.i = 0
357 }
358 func (k *Int32Key) random(r *rand.Rand) {
359 k.i = r.Uint32()
360 }
361 func (k *Int32Key) bits() int {
362 return 32
363 }
364 func (k *Int32Key) flipBit(i int) {
365 k.i ^= 1 << uint(i)
366 }
367 func (k *Int32Key) hash() uintptr {
368 return Int32Hash(k.i, 0)
369 }
370 func (k *Int32Key) name() string {
371 return "int32"
372 }
373
374 type Int64Key struct {
375 i uint64
376 }
377
378 func (k *Int64Key) clear() {
379 k.i = 0
380 }
381 func (k *Int64Key) random(r *rand.Rand) {
382 k.i = uint64(r.Uint32()) + uint64(r.Uint32())<<32
383 }
384 func (k *Int64Key) bits() int {
385 return 64
386 }
387 func (k *Int64Key) flipBit(i int) {
388 k.i ^= 1 << uint(i)
389 }
390 func (k *Int64Key) hash() uintptr {
391 return Int64Hash(k.i, 0)
392 }
393 func (k *Int64Key) name() string {
394 return "int64"
395 }
396
397 type EfaceKey struct {
398 i any
399 }
400
401 func (k *EfaceKey) clear() {
402 k.i = nil
403 }
404 func (k *EfaceKey) random(r *rand.Rand) {
405 k.i = uint64(r.Int63())
406 }
407 func (k *EfaceKey) bits() int {
408
409
410
411 return 64
412 }
413 func (k *EfaceKey) flipBit(i int) {
414 k.i = k.i.(uint64) ^ uint64(1)<<uint(i)
415 }
416 func (k *EfaceKey) hash() uintptr {
417 return EfaceHash(k.i, 0)
418 }
419 func (k *EfaceKey) name() string {
420 return "Eface"
421 }
422
423 type IfaceKey struct {
424 i interface {
425 F()
426 }
427 }
428 type fInter uint64
429
430 func (x fInter) F() {
431 }
432
433 func (k *IfaceKey) clear() {
434 k.i = nil
435 }
436 func (k *IfaceKey) random(r *rand.Rand) {
437 k.i = fInter(r.Int63())
438 }
439 func (k *IfaceKey) bits() int {
440
441
442
443 return 64
444 }
445 func (k *IfaceKey) flipBit(i int) {
446 k.i = k.i.(fInter) ^ fInter(1)<<uint(i)
447 }
448 func (k *IfaceKey) hash() uintptr {
449 return IfaceHash(k.i, 0)
450 }
451 func (k *IfaceKey) name() string {
452 return "Iface"
453 }
454
455
456 func TestSmhasherAvalanche(t *testing.T) {
457 if GOARCH == "wasm" {
458 t.Skip("Too slow on wasm")
459 }
460 if testing.Short() {
461 t.Skip("Skipping in short mode")
462 }
463 if race.Enabled {
464 t.Skip("Too long for race mode")
465 }
466 avalancheTest1(t, &BytesKey{make([]byte, 2)})
467 avalancheTest1(t, &BytesKey{make([]byte, 4)})
468 avalancheTest1(t, &BytesKey{make([]byte, 8)})
469 avalancheTest1(t, &BytesKey{make([]byte, 16)})
470 avalancheTest1(t, &BytesKey{make([]byte, 32)})
471 avalancheTest1(t, &BytesKey{make([]byte, 200)})
472 avalancheTest1(t, &Int32Key{})
473 avalancheTest1(t, &Int64Key{})
474 avalancheTest1(t, &EfaceKey{})
475 avalancheTest1(t, &IfaceKey{})
476 }
477 func avalancheTest1(t *testing.T, k Key) {
478 const REP = 100000
479 r := rand.New(rand.NewSource(1234))
480 n := k.bits()
481
482
483
484 grid := make([][hashSize]int, n)
485
486 for z := 0; z < REP; z++ {
487
488 k.random(r)
489 h := k.hash()
490
491
492 for i := 0; i < n; i++ {
493 k.flipBit(i)
494 d := h ^ k.hash()
495 k.flipBit(i)
496
497
498 g := &grid[i]
499 for j := 0; j < hashSize; j++ {
500 g[j] += int(d & 1)
501 d >>= 1
502 }
503 }
504 }
505
506
507
508
509
510
511 N := n * hashSize
512 var c float64
513
514 for c = 0.0; math.Pow(math.Erf(c/math.Sqrt(2)), float64(N)) < .9999; c += .1 {
515 }
516 c *= 11.0
517 mean := .5 * REP
518 stddev := .5 * math.Sqrt(REP)
519 low := int(mean - c*stddev)
520 high := int(mean + c*stddev)
521 for i := 0; i < n; i++ {
522 for j := 0; j < hashSize; j++ {
523 x := grid[i][j]
524 if x < low || x > high {
525 t.Errorf("bad bias for %s bit %d -> bit %d: %d/%d\n", k.name(), i, j, x, REP)
526 }
527 }
528 }
529 }
530
531
532 func TestSmhasherWindowed(t *testing.T) {
533 if race.Enabled {
534 t.Skip("Too long for race mode")
535 }
536 t.Logf("32 bit keys")
537 windowed(t, &Int32Key{})
538 t.Logf("64 bit keys")
539 windowed(t, &Int64Key{})
540 t.Logf("string keys")
541 windowed(t, &BytesKey{make([]byte, 128)})
542 }
543 func windowed(t *testing.T, k Key) {
544 if GOARCH == "wasm" {
545 t.Skip("Too slow on wasm")
546 }
547 if PtrSize == 4 {
548
549
550
551
552 t.Skip("Flaky on 32-bit systems")
553 }
554 if testing.Short() {
555 t.Skip("Skipping in short mode")
556 }
557 const BITS = 16
558
559 for r := 0; r < k.bits(); r++ {
560 h := newHashSet()
561 for i := 0; i < 1<<BITS; i++ {
562 k.clear()
563 for j := 0; j < BITS; j++ {
564 if i>>uint(j)&1 != 0 {
565 k.flipBit((j + r) % k.bits())
566 }
567 }
568 h.add(k.hash())
569 }
570 h.check(t)
571 }
572 }
573
574
575 func TestSmhasherText(t *testing.T) {
576 if testing.Short() {
577 t.Skip("Skipping in short mode")
578 }
579 text(t, "Foo", "Bar")
580 text(t, "FooBar", "")
581 text(t, "", "FooBar")
582 }
583 func text(t *testing.T, prefix, suffix string) {
584 const N = 4
585 const S = "ABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrst0123456789"
586 const L = len(S)
587 b := make([]byte, len(prefix)+N+len(suffix))
588 copy(b, prefix)
589 copy(b[len(prefix)+N:], suffix)
590 h := newHashSet()
591 c := b[len(prefix):]
592 for i := 0; i < L; i++ {
593 c[0] = S[i]
594 for j := 0; j < L; j++ {
595 c[1] = S[j]
596 for k := 0; k < L; k++ {
597 c[2] = S[k]
598 for x := 0; x < L; x++ {
599 c[3] = S[x]
600 h.addB(b)
601 }
602 }
603 }
604 }
605 h.check(t)
606 }
607
608
609 func TestSmhasherSeed(t *testing.T) {
610 h := newHashSet()
611 const N = 100000
612 s := "hello"
613 for i := 0; i < N; i++ {
614 h.addS_seed(s, uintptr(i))
615 }
616 h.check(t)
617 }
618
619
620 const hashSize = 32 + int(^uintptr(0)>>63<<5)
621
622 func randBytes(r *rand.Rand, b []byte) {
623 for i := range b {
624 b[i] = byte(r.Uint32())
625 }
626 }
627
628 func benchmarkHash(b *testing.B, n int) {
629 s := strings.Repeat("A", n)
630
631 for i := 0; i < b.N; i++ {
632 StringHash(s, 0)
633 }
634 b.SetBytes(int64(n))
635 }
636
637 func BenchmarkHash5(b *testing.B) { benchmarkHash(b, 5) }
638 func BenchmarkHash16(b *testing.B) { benchmarkHash(b, 16) }
639 func BenchmarkHash64(b *testing.B) { benchmarkHash(b, 64) }
640 func BenchmarkHash1024(b *testing.B) { benchmarkHash(b, 1024) }
641 func BenchmarkHash65536(b *testing.B) { benchmarkHash(b, 65536) }
642
643 func TestArrayHash(t *testing.T) {
644
645
646
647
648
649
650
651
652
653
654
655 f := func() {
656
657
658 type key [8]string
659 m := make(map[key]bool, 70)
660
661
662 for i := 0; i < 256; i++ {
663 var k key
664 cnt := 0
665 for j := uint(0); j < 8; j++ {
666 if i>>j&1 != 0 {
667 k[j] = "foo"
668 cnt++
669 }
670 }
671 if cnt == 4 {
672 m[k] = true
673 }
674 }
675 if len(m) != 70 {
676 t.Errorf("bad test: (8 choose 4) should be 70, not %d", len(m))
677 }
678 }
679 if n := testing.AllocsPerRun(10, f); n > 6 {
680 t.Errorf("too many allocs %f - hash not balanced", n)
681 }
682 }
683 func TestStructHash(t *testing.T) {
684
685 f := func() {
686 type key struct {
687 a, b, c, d, e, f, g, h string
688 }
689 m := make(map[key]bool, 70)
690
691
692 for i := 0; i < 256; i++ {
693 var k key
694 cnt := 0
695 if i&1 != 0 {
696 k.a = "foo"
697 cnt++
698 }
699 if i&2 != 0 {
700 k.b = "foo"
701 cnt++
702 }
703 if i&4 != 0 {
704 k.c = "foo"
705 cnt++
706 }
707 if i&8 != 0 {
708 k.d = "foo"
709 cnt++
710 }
711 if i&16 != 0 {
712 k.e = "foo"
713 cnt++
714 }
715 if i&32 != 0 {
716 k.f = "foo"
717 cnt++
718 }
719 if i&64 != 0 {
720 k.g = "foo"
721 cnt++
722 }
723 if i&128 != 0 {
724 k.h = "foo"
725 cnt++
726 }
727 if cnt == 4 {
728 m[k] = true
729 }
730 }
731 if len(m) != 70 {
732 t.Errorf("bad test: (8 choose 4) should be 70, not %d", len(m))
733 }
734 }
735 if n := testing.AllocsPerRun(10, f); n > 6 {
736 t.Errorf("too many allocs %f - hash not balanced", n)
737 }
738 }
739
740 var sink uint64
741
742 func BenchmarkAlignedLoad(b *testing.B) {
743 var buf [16]byte
744 p := unsafe.Pointer(&buf[0])
745 var s uint64
746 for i := 0; i < b.N; i++ {
747 s += ReadUnaligned64(p)
748 }
749 sink = s
750 }
751
752 func BenchmarkUnalignedLoad(b *testing.B) {
753 var buf [16]byte
754 p := unsafe.Pointer(&buf[1])
755 var s uint64
756 for i := 0; i < b.N; i++ {
757 s += ReadUnaligned64(p)
758 }
759 sink = s
760 }
761
762 func TestCollisions(t *testing.T) {
763 if testing.Short() {
764 t.Skip("Skipping in short mode")
765 }
766 for i := 0; i < 16; i++ {
767 for j := 0; j < 16; j++ {
768 if j == i {
769 continue
770 }
771 var a [16]byte
772 m := make(map[uint16]struct{}, 1<<16)
773 for n := 0; n < 1<<16; n++ {
774 a[i] = byte(n)
775 a[j] = byte(n >> 8)
776 m[uint16(BytesHash(a[:], 0))] = struct{}{}
777 }
778
779 avg := 41427
780 stdDev := 123
781 if len(m) < avg-40*stdDev || len(m) > avg+40*stdDev {
782 t.Errorf("bad number of collisions i=%d j=%d outputs=%d out of 65536\n", i, j, len(m))
783 }
784 }
785 }
786 }
787
View as plain text