util/deephash: avoid using sync.Pool for reflect.MapIter (#5333)

In Go 1.19, the reflect.Value.MapRange method uses "function outlining"
so that the allocation of reflect.MapIter is inlinable by the caller.
If the iterator doesn't escape the caller, it can be stack allocated.
See https://go.dev/cl/400675

Performance:

	name               old time/op    new time/op    delta
	HashMapAcyclic-24    31.9µs ± 2%    32.1µs ± 1%   ~     (p=0.075 n=10+10)

	name               old alloc/op   new alloc/op   delta
	HashMapAcyclic-24     0.00B          0.00B        ~     (all equal)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
This commit is contained in:
Joe Tsai 2022-08-11 00:33:40 -07:00 committed by GitHub
parent 5d731ca13f
commit 77a92f326d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -919,7 +919,6 @@ func (h *hasher) hashValueWithType(v addressableValue, ti *typeInfo, forceCycleC
type mapHasher struct {
h hasher
valKey, valElem valueCache // re-usable values for map iteration
iter reflect.MapIter // re-usable map iterator
}
var mapHasherPool = &sync.Pool{
@ -948,10 +947,6 @@ func (h *hasher) hashMap(v addressableValue, ti *typeInfo, checkCycles bool) {
mh := mapHasherPool.Get().(*mapHasher)
defer mapHasherPool.Put(mh)
iter := &mh.iter
iter.Reset(v.Value)
defer iter.Reset(reflect.Value{}) // avoid pinning v from mh.iter when we return
var sum Sum
if v.IsNil() {
sum.sum[0] = 1 // something non-zero
@ -960,7 +955,7 @@ func (h *hasher) hashMap(v addressableValue, ti *typeInfo, checkCycles bool) {
k := mh.valKey.get(v.Type().Key())
e := mh.valElem.get(v.Type().Elem())
mh.h.visitStack = h.visitStack // always use the parent's visit stack to avoid cycles
for iter.Next() {
for iter := v.MapRange(); iter.Next(); {
k.SetIterKey(iter)
e.SetIterValue(iter)
mh.h.reset()