internal/deephash: add special handling for netaddr.IPPort

The acyclic map code interacts badly with netaddr.IPs.
One of the netaddr.IP fields is an *intern.Value,
and we use a few sentinel values.
Those sentinel values make many of the netaddr data structures appear cyclic.

One option would be to replace the cycle-detection code with
a Floyd-Warshall style algorithm. The downside is that this will take
longer to detect cycles, particularly if the cycle is long.

This problem is exacerbated by the fact that the acyclic cycle detection
code shares a single visited map for the entire data structure,
not just the subsection of the data structure localized to the map.
Unfortunately, the extra allocations and work (and code) to use per-map
visited maps make this option not viable.

Instead, continue to special-case netaddr data types.

name              old time/op    new time/op    delta
Hash-8              22.4µs ± 0%    14.0µs ± 0%  -37.59%  (p=0.008 n=5+5)
HashMapAcyclic-8    23.8µs ± 0%    24.3µs ± 1%   +1.75%  (p=0.008 n=5+5)

name              old alloc/op   new alloc/op   delta
Hash-8              2.49kB ± 0%    2.16kB ± 0%     ~     (p=0.079 n=4+5)
HashMapAcyclic-8    2.53kB ± 0%    2.53kB ± 0%     ~     (all equal)

name              old allocs/op  new allocs/op  delta
Hash-8                86.0 ± 0%      77.0 ± 0%  -10.47%  (p=0.008 n=5+5)
HashMapAcyclic-8       202 ± 0%       202 ± 0%     ~     (all equal)

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
This commit is contained in:
Josh Bleecher Snyder 2021-05-18 09:07:11 -07:00 committed by Josh Bleecher Snyder
parent bbb79f2d6a
commit 020e904f4e

View File

@ -45,6 +45,7 @@ func Print(w *bufio.Writer, v ...interface{}) {
var (
netaddrIPType = reflect.TypeOf(netaddr.IP{})
netaddrIPPrefix = reflect.TypeOf(netaddr.IPPrefix{})
netaddrIPPort = reflect.TypeOf(netaddr.IPPort{})
wgkeyKeyType = reflect.TypeOf(wgkey.Key{})
wgkeyPrivateType = reflect.TypeOf(wgkey.Private{})
tailcfgDiscoKeyType = reflect.TypeOf(tailcfg.DiscoKey{})
@ -88,6 +89,20 @@ func print(w *bufio.Writer, v reflect.Value, visited map[uintptr]bool) (acyclic
w.Write(b)
return true
}
case netaddrIPPort:
var b []byte
var err error
if v.CanAddr() {
x := v.Addr().Interface().(*netaddr.IPPort)
b, err = x.MarshalText()
} else {
x := v.Interface().(netaddr.IPPort)
b, err = x.MarshalText()
}
if err == nil {
w.Write(b)
return true
}
case wgkeyKeyType:
if v.CanAddr() {
x := v.Addr().Interface().(*wgkey.Key)