From 09afb8e35b56c1150ae9872d781c7ae3c151b848 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 18 May 2021 09:20:52 -0700 Subject: [PATCH] internal/deephash: re-use map iteration values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This requires changes to the Go toolchain. The changes are upstream at https://golang.org/cl/320929. They haven't been pulled into our fork yet. No need to allocate new iteration scratch values for every map. name old time/op new time/op delta Hash-8 13.6µs ± 0% 13.5µs ± 0% -1.01% (p=0.008 n=5+5) HashMapAcyclic-8 21.2µs ± 1% 21.1µs ± 2% ~ (p=0.310 n=5+5) name old alloc/op new alloc/op delta Hash-8 1.58kB ± 0% 1.46kB ± 0% -7.60% (p=0.008 n=5+5) HashMapAcyclic-8 152B ± 0% 128B ± 0% -15.79% (p=0.008 n=5+5) name old allocs/op new allocs/op delta Hash-8 49.0 ± 0% 43.0 ± 0% -12.24% (p=0.008 n=5+5) HashMapAcyclic-8 4.00 ± 0% 2.00 ± 0% -50.00% (p=0.008 n=5+5) Signed-off-by: Josh Bleecher Snyder --- internal/deephash/deephash.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/internal/deephash/deephash.go b/internal/deephash/deephash.go index 16b89ac4e..a15e0744e 100644 --- a/internal/deephash/deephash.go +++ b/internal/deephash/deephash.go @@ -200,6 +200,7 @@ type mapHasher struct { ebuf [sha256.Size]byte // scratch buffer s256 hash.Hash // sha256 hash.Hash bw *bufio.Writer // to hasher into ebuf + val valueCache // re-usable values for map iteration } func (mh *mapHasher) Reset() { @@ -228,10 +229,22 @@ var mapHasherPool = &sync.Pool{ mh := new(mapHasher) mh.s256 = sha256.New() mh.bw = bufio.NewWriter(mh.s256) + mh.val = make(valueCache) return mh }, } +type valueCache map[reflect.Type]reflect.Value + +func (c valueCache) get(t reflect.Type) reflect.Value { + v, ok := c[t] + if !ok { + v = reflect.New(t).Elem() + c[t] = v + } + return v +} + // hashMapAcyclic is the faster sort-free version of map hashing. If // it detects a cycle it returns false and guarantees that nothing was // written to w. @@ -240,8 +253,8 @@ func hashMapAcyclic(w *bufio.Writer, v reflect.Value, visited map[uintptr]bool) defer mapHasherPool.Put(mh) mh.Reset() iter := v.MapRange() - k := reflect.New(v.Type().Key()).Elem() - e := reflect.New(v.Type().Elem()).Elem() + k := mh.val.get(v.Type().Key()) + e := mh.val.get(v.Type().Elem()) for iter.Next() { key := iterKey(iter, k) val := iterVal(iter, e)