github.com/puzpuzpuz/xsync/v2@v2.5.2-0.20231021165734-92b8269e19a9/hashing_test.go (about) 1 //go:build go1.18 2 // +build go1.18 3 4 package xsync 5 6 import ( 7 "fmt" 8 "hash/maphash" 9 "testing" 10 ) 11 12 func TestMakeHashFunc(t *testing.T) { 13 type User struct { 14 Name string 15 City string 16 } 17 18 seed := maphash.MakeSeed() 19 20 hashString := makeHashFunc[string]() 21 hashUser := makeHashFunc[User]() 22 hashAny := makeHashFunc[any]() // this declaration requires go 1.20+, though makeHashFunc itself can work with go.18+ 23 24 hashUserNative := makeHashFuncNative[User]() 25 26 // Not that much to test TBH. 27 28 // check that hash is not always the same 29 for i := 0; ; i++ { 30 if hashString(seed, "foo") != hashString(seed, "bar") { 31 break 32 } 33 if i >= 100 { 34 t.Error("hashString is always the same") 35 break 36 } 37 38 seed = maphash.MakeSeed() // try with a new seed 39 } 40 41 // do the same for hash any 42 for i := 0; ; i++ { 43 if hashAny(seed, "foo") != hashAny(seed, "bar") { 44 break 45 } 46 if i >= 100 { 47 t.Error("hashAny is always the same") 48 break 49 } 50 51 seed = maphash.MakeSeed() // try with a new seed 52 } 53 54 if hashString(seed, "foo") != hashString(seed, "foo") { 55 t.Error("hashString is not deterministic") 56 } 57 58 if hashUser(seed, User{Name: "John", City: "New York"}) != hashUser(seed, User{Name: "John", City: "New York"}) { 59 t.Error("hashUser is not deterministic") 60 } 61 62 if hashAny(seed, User{Name: "John", City: "New York"}) != hashAny(seed, User{Name: "John", City: "New York"}) { 63 t.Error("hashAny is not deterministic") 64 } 65 66 // just for fun, compare with native hash function 67 if hashUser(seed, User{Name: "John", City: "New York"}) != hashUserNative(seed, User{Name: "John", City: "New York"}) { 68 t.Error("hashUser and hashUserNative return different values") 69 } 70 71 } 72 73 func expectEqualHashes[T comparable](t *testing.T, val1, val2 T) { 74 t.Helper() 75 76 if val1 != val2 { 77 t.Error("use expectDifferentHashes for non-equal values") 78 return 79 } 80 81 hash := makeHashFunc[T]() 82 seed := maphash.MakeSeed() 83 84 if hash(seed, val1) != hash(seed, val2) { 85 t.Error("two invocations of hash for the same value return different results") 86 } 87 } 88 89 func BenchmarkMakeHashFunc(b *testing.B) { 90 type Point struct { 91 X, Y, Z int 92 } 93 94 type User struct { 95 ID int 96 FirstName string 97 LastName string 98 IsActive bool 99 City string 100 } 101 102 type PadInside struct { 103 A int 104 B byte 105 C int 106 } 107 108 type PadTrailing struct { 109 A int 110 B byte 111 } 112 113 doBenchmarkMakeHashFunc(b, int64(116)) 114 doBenchmarkMakeHashFunc(b, int32(116)) 115 doBenchmarkMakeHashFunc(b, 3.14) 116 doBenchmarkMakeHashFunc(b, "test key test key test key test key test key test key test key test key test key ") 117 doBenchmarkMakeHashFunc(b, Point{1, 2, 3}) 118 doBenchmarkMakeHashFunc(b, User{ID: 1, FirstName: "John", LastName: "Smith", IsActive: true, City: "New York"}) 119 doBenchmarkMakeHashFunc(b, PadInside{}) 120 doBenchmarkMakeHashFunc(b, PadTrailing{}) 121 doBenchmarkMakeHashFunc(b, [1024]byte{}) 122 doBenchmarkMakeHashFunc(b, [128]Point{}) 123 doBenchmarkMakeHashFunc(b, [128]User{}) 124 doBenchmarkMakeHashFunc(b, [128]PadInside{}) 125 doBenchmarkMakeHashFunc(b, [128]PadTrailing{}) 126 } 127 128 func doBenchmarkMakeHashFunc[T comparable](b *testing.B, val T) { 129 hash := makeHashFunc[T]() 130 hashDry := makeHashFuncDRY[T]() 131 hashNative := makeHashFuncNative[T]() 132 seed := maphash.MakeSeed() 133 134 b.Run(fmt.Sprintf("%T normal", val), func(b *testing.B) { 135 b.ReportAllocs() 136 for i := 0; i < b.N; i++ { 137 _ = hash(seed, val) 138 } 139 }) 140 141 b.Run(fmt.Sprintf("%T dry", val), func(b *testing.B) { 142 b.ReportAllocs() 143 for i := 0; i < b.N; i++ { 144 _ = hashDry(seed, val) 145 } 146 }) 147 148 b.Run(fmt.Sprintf("%T native", val), func(b *testing.B) { 149 b.ReportAllocs() 150 for i := 0; i < b.N; i++ { 151 _ = hashNative(seed, val) 152 } 153 }) 154 }