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  }