diff --git a/sql/cache.go b/sql/cache.go index 32861e9e37..94759be248 100644 --- a/sql/cache.go +++ b/sql/cache.go @@ -17,6 +17,7 @@ package sql import ( "fmt" "runtime" + "sync" "github.com/cespare/xxhash/v2" @@ -25,7 +26,9 @@ import ( // HashOf returns a hash of the given value to be used as key in a cache. func HashOf(v Row) (uint64, error) { - hash := xxhash.New() + hash := digestPool.Get().(*xxhash.Digest) + hash.Reset() + defer digestPool.Put(hash) for i, x := range v { if i > 0 { // separate each value in the row with a nil byte @@ -38,13 +41,19 @@ func HashOf(v Row) (uint64, error) { // TODO: we don't have the type info necessary to appropriately encode the value of a string with a non-standard // collation, which means that two strings that differ only in their collations will hash to the same value. // See rowexec/grouping_key() - if _, err := hash.Write([]byte(fmt.Sprintf("%v,", x))); err != nil { + if _, err := fmt.Fprintf(hash, "%v,", x); err != nil { return 0, err } } return hash.Sum64(), nil } +var digestPool = sync.Pool{ + New: func() any { + return xxhash.New() + }, +} + // ErrKeyNotFound is returned when the key could not be found in the cache. var ErrKeyNotFound = fmt.Errorf("memory: key not found in cache") diff --git a/sql/cache_test.go b/sql/cache_test.go index 1f6dd58f43..c93e04ad1b 100644 --- a/sql/cache_test.go +++ b/sql/cache_test.go @@ -177,3 +177,33 @@ func TestRowsCache(t *testing.T) { require.True(freed) }) } + +func BenchmarkHashOf(b *testing.B) { + row := NewRow(1, "1") + b.ResetTimer() + for i := 0; i < b.N; i++ { + sum, err := HashOf(row) + if err != nil { + b.Fatal(err) + } + if sum != 11268758894040352165 { + b.Fatalf("got %v", sum) + } + } +} + +func BenchmarkParallelHashOf(b *testing.B) { + row := NewRow(1, "1") + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + sum, err := HashOf(row) + if err != nil { + b.Fatal(err) + } + if sum != 11268758894040352165 { + b.Fatalf("got %v", sum) + } + } + }) +}