From 6e68e08af157ddf02fe80e3f0909197c5de7d7f8 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Mon, 16 Jan 2023 14:13:11 -0800 Subject: [PATCH] [coreinternal/pdatautil] Reduce number of allocations in hash functions --- internal/coreinternal/pdatautil/hash.go | 115 +++++++++++++++--------- 1 file changed, 72 insertions(+), 43 deletions(-) diff --git a/internal/coreinternal/pdatautil/hash.go b/internal/coreinternal/pdatautil/hash.go index 39f9f7ca5b23..2657c41edc08 100644 --- a/internal/coreinternal/pdatautil/hash.go +++ b/internal/coreinternal/pdatautil/hash.go @@ -19,6 +19,7 @@ import ( "hash" "math" "sort" + "sync" "github.com/cespare/xxhash/v2" "go.opentelemetry.io/collector/pdata/pcommon" @@ -40,87 +41,115 @@ var ( valSliceSuffix = []byte{'\xff'} ) +type hashWriter struct { + h hash.Hash + strBuf []byte + keysBuf []string + sumHash []byte + numBuf []byte +} + +func newHashWriter() *hashWriter { + return &hashWriter{ + h: xxhash.New(), + strBuf: make([]byte, 0, 128), + keysBuf: make([]string, 0, 16), + sumHash: make([]byte, 0, 16), + numBuf: make([]byte, 8), + } +} + +var hashWriterPool = &sync.Pool{ + New: func() interface{} { return newHashWriter() }, +} + // MapHash return a hash for the provided map. // Maps with the same underlying key/value pairs in different order produce the same deterministic hash value. func MapHash(m pcommon.Map) [16]byte { - h := xxhash.New() - writeMapHash(h, m) - return hashSum128(h) + hw := hashWriterPool.Get().(*hashWriter) + defer hashWriterPool.Put(hw) + hw.h.Reset() + hw.writeMapHash(m) + return hw.hashSum128() } // ValueHash return a hash for the provided pcommon.Value. func ValueHash(v pcommon.Value) [16]byte { - h := xxhash.New() - writeValueHash(h, v) - return hashSum128(h) + hw := hashWriterPool.Get().(*hashWriter) + defer hashWriterPool.Put(hw) + hw.h.Reset() + hw.writeValueHash(v) + return hw.hashSum128() } -func writeMapHash(h hash.Hash, m pcommon.Map) { - keys := make([]string, 0, m.Len()) +func (hw *hashWriter) writeMapHash(m pcommon.Map) { + hw.keysBuf = hw.keysBuf[:0] m.Range(func(k string, v pcommon.Value) bool { - keys = append(keys, k) + hw.keysBuf = append(hw.keysBuf, k) return true }) - sort.Strings(keys) - for _, k := range keys { + sort.Strings(hw.keysBuf) + for _, k := range hw.keysBuf { v, _ := m.Get(k) - h.Write(keyPrefix) - h.Write([]byte(k)) - writeValueHash(h, v) + hw.strBuf = hw.strBuf[:0] + hw.strBuf = append(hw.strBuf, keyPrefix...) + hw.strBuf = append(hw.strBuf, k...) + hw.h.Write(hw.strBuf) + hw.writeValueHash(v) } } -func writeSliceHash(h hash.Hash, sl pcommon.Slice) { +func (hw *hashWriter) writeSliceHash(sl pcommon.Slice) { for i := 0; i < sl.Len(); i++ { - writeValueHash(h, sl.At(i)) + hw.writeValueHash(sl.At(i)) } } -func writeValueHash(h hash.Hash, v pcommon.Value) { +func (hw *hashWriter) writeValueHash(v pcommon.Value) { switch v.Type() { case pcommon.ValueTypeStr: - h.Write(valStrPrefix) - h.Write([]byte(v.Str())) + hw.strBuf = hw.strBuf[:0] + hw.strBuf = append(hw.strBuf, valStrPrefix...) + hw.strBuf = append(hw.strBuf, v.Str()...) + hw.h.Write(hw.strBuf) case pcommon.ValueTypeBool: if v.Bool() { - h.Write(valBoolTrue) + hw.h.Write(valBoolTrue) } else { - h.Write(valBoolFalse) + hw.h.Write(valBoolFalse) } case pcommon.ValueTypeInt: - h.Write(valIntPrefix) - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, uint64(v.Int())) - h.Write(b) + hw.h.Write(valIntPrefix) + binary.LittleEndian.PutUint64(hw.numBuf, uint64(v.Int())) + hw.h.Write(hw.numBuf) case pcommon.ValueTypeDouble: - h.Write(valDoublePrefix) - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, math.Float64bits(v.Double())) - h.Write(b) + hw.h.Write(valDoublePrefix) + binary.LittleEndian.PutUint64(hw.numBuf, math.Float64bits(v.Double())) + hw.h.Write(hw.numBuf) case pcommon.ValueTypeMap: - h.Write(valMapPrefix) - writeMapHash(h, v.Map()) - h.Write(valMapSuffix) + hw.h.Write(valMapPrefix) + hw.writeMapHash(v.Map()) + hw.h.Write(valMapSuffix) case pcommon.ValueTypeSlice: - h.Write(valSlicePrefix) - writeSliceHash(h, v.Slice()) - h.Write(valSliceSuffix) + hw.h.Write(valSlicePrefix) + hw.writeSliceHash(v.Slice()) + hw.h.Write(valSliceSuffix) case pcommon.ValueTypeBytes: - h.Write(valBytesPrefix) - h.Write(v.Bytes().AsRaw()) + hw.h.Write(valBytesPrefix) + hw.h.Write(v.Bytes().AsRaw()) case pcommon.ValueTypeEmpty: - h.Write(valEmpty) + hw.h.Write(valEmpty) } } // hashSum128 returns a [16]byte hash sum. -func hashSum128(h hash.Hash) [16]byte { - b := make([]byte, 0, 16) - b = h.Sum(b) +func (hw *hashWriter) hashSum128() [16]byte { + b := hw.sumHash[:0] + b = hw.h.Sum(b) // Append an extra byte to generate another part of the hash sum - _, _ = h.Write(extraByte) - b = h.Sum(b) + _, _ = hw.h.Write(extraByte) + b = hw.h.Sum(b) res := [16]byte{} copy(res[:], b)