mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-05 04:11:59 +00:00
util/deephash: generate type-specific hasher funcs
name old time/op new time/op delta Hash-8 71.1µs ± 2% 71.5µs ± 1% ~ (p=0.114 n=9+8) HashPacketFilter-8 8.39µs ± 1% 4.83µs ± 2% -42.38% (p=0.000 n=8+9) HashMapAcyclic-8 56.2µs ± 1% 56.9µs ± 2% +1.17% (p=0.035 n=10+9) TailcfgNode-8 6.49µs ± 2% 3.54µs ± 1% -45.37% (p=0.000 n=9+9) HashArray-8 729ns ± 2% 566ns ± 3% -22.30% (p=0.000 n=10+10) name old alloc/op new alloc/op delta Hash-8 24.0B ± 0% 24.0B ± 0% ~ (all equal) HashPacketFilter-8 24.0B ± 0% 24.0B ± 0% ~ (all equal) HashMapAcyclic-8 0.00B 0.00B ~ (all equal) TailcfgNode-8 0.00B 0.00B ~ (all equal) HashArray-8 0.00B 0.00B ~ (all equal) name old allocs/op new allocs/op delta Hash-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) HashPacketFilter-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) HashMapAcyclic-8 0.00 0.00 ~ (all equal) TailcfgNode-8 0.00 0.00 ~ (all equal) HashArray-8 0.00 0.00 ~ (all equal) Change-Id: I34c4e786e748fe60280646d40cc63a2adb2ea6fe Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
4d0461f721
commit
2a22ea3e83
@@ -392,6 +392,238 @@ func TestCanMemHash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTypeHasher(t *testing.T) {
|
||||
switch runtime.GOARCH {
|
||||
case "amd64", "arm64", "arm", "386", "riscv64":
|
||||
default:
|
||||
// Test outputs below are specifically for little-endian machines.
|
||||
// Just skip everything else for now. Feel free to add more above if
|
||||
// you have the hardware to test and it's little-endian.
|
||||
t.Skipf("skipping on %v", runtime.GOARCH)
|
||||
}
|
||||
type typedString string
|
||||
var (
|
||||
someInt = int('A')
|
||||
someComplex128 = complex128(1 + 2i)
|
||||
someIP = netaddr.MustParseIP("1.2.3.4")
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
val any
|
||||
want bool // set true automatically if out != ""
|
||||
out string
|
||||
out32 string // overwrites out if 32-bit
|
||||
}{
|
||||
{
|
||||
name: "int",
|
||||
val: int(1),
|
||||
out: "\x01\x00\x00\x00\x00\x00\x00\x00",
|
||||
},
|
||||
{
|
||||
name: "int_negative",
|
||||
val: int(-1),
|
||||
out: "\xff\xff\xff\xff\xff\xff\xff\xff",
|
||||
},
|
||||
{
|
||||
name: "int8",
|
||||
val: int8(1),
|
||||
out: "\x01",
|
||||
},
|
||||
{
|
||||
name: "float64",
|
||||
val: float64(1.0),
|
||||
out: "\x00\x00\x00\x00\x00\x00\xf0?",
|
||||
},
|
||||
{
|
||||
name: "float32",
|
||||
val: float32(1.0),
|
||||
out: "\x00\x00\x80?",
|
||||
},
|
||||
{
|
||||
name: "string",
|
||||
val: "foo",
|
||||
out: "\x03\x00\x00\x00\x00\x00\x00\x00foo",
|
||||
},
|
||||
{
|
||||
name: "typedString",
|
||||
val: typedString("foo"),
|
||||
out: "\x03\x00\x00\x00\x00\x00\x00\x00foo",
|
||||
},
|
||||
{
|
||||
name: "string_slice",
|
||||
val: []string{"foo", "bar"},
|
||||
out: "\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x03\x00\x00\x00\x00\x00\x00\x00bar",
|
||||
},
|
||||
{
|
||||
name: "int_slice",
|
||||
val: []int{1, 0, -1},
|
||||
out: "\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff",
|
||||
out32: "\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff",
|
||||
},
|
||||
{
|
||||
name: "struct",
|
||||
val: struct {
|
||||
a, b int
|
||||
c uint16
|
||||
}{1, -1, 2},
|
||||
out: "\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x02\x00",
|
||||
},
|
||||
{
|
||||
name: "nil_int_ptr",
|
||||
val: (*int)(nil),
|
||||
out: "\x00",
|
||||
},
|
||||
{
|
||||
name: "int_ptr",
|
||||
val: &someInt,
|
||||
out: "\x01A\x00\x00\x00\x00\x00\x00\x00",
|
||||
out32: "\x01A\x00\x00\x00",
|
||||
},
|
||||
{
|
||||
name: "nil_uint32_ptr",
|
||||
val: (*uint32)(nil),
|
||||
out: "\x00",
|
||||
},
|
||||
{
|
||||
name: "complex128_ptr",
|
||||
val: &someComplex128,
|
||||
out: "\x01\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@",
|
||||
},
|
||||
{
|
||||
name: "packet_filter",
|
||||
val: filterRules,
|
||||
out: "\x04\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00*\v\x00\x00\x00\x00\x00\x00\x0010.1.3.4/32\v\x00\x00\x00\x00\x00\x00\x0010.0.0.0/24\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x001.2.3.4/32\x01 \x00\x00\x00\x00\x00\x00\x00\x01\x00\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x001.2.3.4/32\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
out32: "\x04\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00*\v\x00\x00\x00\x00\x00\x00\x0010.1.3.4/32\v\x00\x00\x00\x00\x00\x00\x0010.0.0.0/24\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x001.2.3.4/32\x01 \x00\x00\x00\x01\x00\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x001.2.3.4/32\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00foo\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x00\x00\x00\x00foooooooooo\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\f\x00\x00\x00\x00\x00\x00\x00baaaaaarrrrr\x00\x01\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
},
|
||||
{
|
||||
name: "netaddr.IP",
|
||||
val: netaddr.MustParseIP("fe80::123%foo"),
|
||||
out: "\r\x00\x00\x00\x00\x00\x00\x00fe80::123%foo",
|
||||
},
|
||||
{
|
||||
name: "ptr-netaddr.IP",
|
||||
val: &someIP,
|
||||
out: "\x01\a\x00\x00\x00\x00\x00\x00\x001.2.3.4",
|
||||
},
|
||||
{
|
||||
name: "ptr-nil-netaddr.IP",
|
||||
val: (*netaddr.IP)(nil),
|
||||
out: "\x00",
|
||||
},
|
||||
{
|
||||
name: "time",
|
||||
val: time.Unix(0, 0).In(time.UTC),
|
||||
out: "\x141970-01-01T00:00:00Z",
|
||||
},
|
||||
{
|
||||
name: "time_custom_zone",
|
||||
val: time.Unix(1655311822, 0).In(time.FixedZone("FOO", -60*60)),
|
||||
out: "\x192022-06-15T15:50:22-01:00",
|
||||
},
|
||||
{
|
||||
name: "time_nil",
|
||||
val: (*time.Time)(nil),
|
||||
out: "\x00",
|
||||
},
|
||||
{
|
||||
name: "array_memhash",
|
||||
val: [4]byte{1, 2, 3, 4},
|
||||
out: "\x01\x02\x03\x04",
|
||||
},
|
||||
{
|
||||
name: "array_ptr_memhash",
|
||||
val: ptrTo([4]byte{1, 2, 3, 4}),
|
||||
out: "\x01\x01\x02\x03\x04",
|
||||
},
|
||||
{
|
||||
name: "ptr_to_struct_partially_memhashable",
|
||||
val: &struct {
|
||||
A int16
|
||||
B int16
|
||||
C *int
|
||||
}{5, 6, nil},
|
||||
out: "\x01\x05\x00\x06\x00\x00",
|
||||
},
|
||||
{
|
||||
name: "struct_partially_memhashable_but_cant_addr",
|
||||
val: struct {
|
||||
A int16
|
||||
B int16
|
||||
C *int
|
||||
}{5, 6, nil},
|
||||
out: "\x05\x00\x06\x00\x00",
|
||||
},
|
||||
{
|
||||
name: "array_elements",
|
||||
val: [4]byte{1, 2, 3, 4},
|
||||
out: "\x01\x02\x03\x04",
|
||||
},
|
||||
{
|
||||
name: "bool",
|
||||
val: true,
|
||||
out: "\x01",
|
||||
},
|
||||
{
|
||||
name: "IntIntByteInt",
|
||||
val: IntIntByteInt{1, 2, 3, 4},
|
||||
out: "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00",
|
||||
},
|
||||
{
|
||||
name: "IntIntByteInt-canddr",
|
||||
val: &IntIntByteInt{1, 2, 3, 4},
|
||||
out: "\x01\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00",
|
||||
},
|
||||
{
|
||||
name: "array-IntIntByteInt",
|
||||
val: [2]IntIntByteInt{
|
||||
{1, 2, 3, 4},
|
||||
{5, 6, 7, 8},
|
||||
},
|
||||
out: "\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\a\b\x00\x00\x00",
|
||||
},
|
||||
{
|
||||
name: "array-IntIntByteInt-canaddr",
|
||||
val: &[2]IntIntByteInt{
|
||||
{1, 2, 3, 4},
|
||||
{5, 6, 7, 8},
|
||||
},
|
||||
out: "\x01\x01\x00\x00\x00\x02\x00\x00\x00\x03\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\a\b\x00\x00\x00",
|
||||
},
|
||||
{
|
||||
name: "tailcfg.Node",
|
||||
val: &tailcfg.Node{},
|
||||
out: "\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x140001-01-01T00:00:00Z\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x140001-01-01T00:00:00Z\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rv := reflect.ValueOf(tt.val)
|
||||
fn := getTypeInfo(rv.Type()).hasher()
|
||||
var buf bytes.Buffer
|
||||
h := &hasher{
|
||||
bw: bufio.NewWriter(&buf),
|
||||
}
|
||||
got := fn(h, rv)
|
||||
const ptrSize = 32 << uintptr(^uintptr(0)>>63)
|
||||
if tt.out32 != "" && ptrSize == 32 {
|
||||
tt.out = tt.out32
|
||||
}
|
||||
if tt.out != "" {
|
||||
tt.want = true
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Fatalf("func returned %v; want %v", got, tt.want)
|
||||
}
|
||||
if err := h.bw.Flush(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := buf.String(); got != tt.out {
|
||||
t.Fatalf("got %q; want %q", got, tt.out)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var sink = Hash("foo")
|
||||
|
||||
func BenchmarkHash(b *testing.B) {
|
||||
@@ -448,8 +680,9 @@ var filterRules = []tailcfg.FilterRule{
|
||||
func BenchmarkHashPacketFilter(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
hash := HasherForType[[]tailcfg.FilterRule]()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = Hash(filterRules)
|
||||
sink = hash(filterRules)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user