mirror of
https://github.com/golang/go
synced 2024-11-24 07:20:02 -07:00
reflect: add fast paths for common, simple Kinds to DeepEqual
Though the normal equality check suffices, it allocates. Handle common, simple kinds without allocating. For some real world types compared with DeepEqual in the Tailscale code base, the impact of these changes: name old time/op new time/op delta HostInfoEqual-8 25.9µs ± 0% 14.4µs ± 0% -44.32% (p=0.008 n=5+5) name old alloc/op new alloc/op delta HostInfoEqual-8 18.8kB ± 0% 12.6kB ± 0% -32.58% (p=0.008 n=5+5) name old allocs/op new allocs/op delta HostInfoEqual-8 548 ± 0% 90 ± 0% -83.58% (p=0.008 n=5+5) For the benchmarks added in this commit: name old time/op new time/op delta DeepEqual/int8-8 40.1ns ± 4% 31.3ns ± 4% -21.79% (p=0.000 n=19+20) DeepEqual/[]int8-8 169ns ± 1% 123ns ± 1% -27.05% (p=0.000 n=17+18) DeepEqual/int16-8 39.9ns ± 2% 30.8ns ± 4% -22.81% (p=0.000 n=17+20) DeepEqual/[]int16-8 172ns ± 0% 122ns ± 1% -28.91% (p=0.000 n=17+18) DeepEqual/int32-8 40.4ns ± 3% 31.1ns ± 4% -23.07% (p=0.000 n=20+20) DeepEqual/[]int32-8 180ns ± 1% 124ns ± 1% -31.06% (p=0.000 n=20+18) DeepEqual/int64-8 40.1ns ± 2% 31.4ns ± 4% -21.82% (p=0.000 n=20+20) DeepEqual/[]int64-8 180ns ± 1% 124ns ± 1% -31.47% (p=0.000 n=16+19) DeepEqual/int-8 40.1ns ± 4% 30.9ns ± 3% -22.88% (p=0.000 n=20+18) DeepEqual/[]int-8 180ns ± 0% 123ns ± 2% -31.59% (p=0.000 n=19+20) DeepEqual/uint8-8 39.8ns ± 3% 31.9ns ± 5% -19.72% (p=0.000 n=20+20) DeepEqual/[]uint8-8 168ns ± 1% 114ns ± 1% -32.48% (p=0.000 n=18+19) DeepEqual/uint16-8 40.3ns ± 4% 31.4ns ± 6% -22.14% (p=0.000 n=20+20) DeepEqual/[]uint16-8 173ns ± 1% 124ns ± 1% -28.20% (p=0.000 n=20+16) DeepEqual/uint32-8 40.1ns ± 3% 30.7ns ± 3% -23.48% (p=0.000 n=20+20) DeepEqual/[]uint32-8 180ns ± 1% 123ns ± 1% -31.56% (p=0.000 n=20+18) DeepEqual/uint64-8 40.0ns ± 4% 31.3ns ± 4% -21.80% (p=0.000 n=19+19) DeepEqual/[]uint64-8 180ns ± 1% 124ns ± 0% -31.45% (p=0.000 n=18+18) DeepEqual/uint-8 39.8ns ± 3% 31.1ns ± 4% -21.95% (p=0.000 n=19+20) DeepEqual/[]uint-8 181ns ± 1% 122ns ± 1% -32.33% (p=0.000 n=17+20) DeepEqual/uintptr-8 40.3ns ± 3% 31.2ns ± 3% -22.66% (p=0.000 n=20+18) DeepEqual/[]uintptr-8 181ns ± 1% 124ns ± 1% -31.46% (p=0.000 n=19+16) DeepEqual/float32-8 40.3ns ± 2% 31.2ns ± 3% -22.52% (p=0.000 n=16+20) DeepEqual/[]float32-8 180ns ± 1% 122ns ± 1% -32.18% (p=0.000 n=17+17) DeepEqual/float64-8 40.6ns ± 3% 30.9ns ± 5% -23.91% (p=0.000 n=19+20) DeepEqual/[]float64-8 182ns ± 2% 121ns ± 1% -33.33% (p=0.000 n=18+20) DeepEqual/complex64-8 43.0ns ±11% 32.1ns ± 5% -25.33% (p=0.000 n=20+18) DeepEqual/[]complex64-8 182ns ± 1% 122ns ± 2% -32.60% (p=0.000 n=18+19) DeepEqual/complex128-8 42.4ns ± 4% 34.2ns ± 3% -19.35% (p=0.000 n=20+19) DeepEqual/[]complex128-8 197ns ± 1% 122ns ± 1% -38.01% (p=0.000 n=19+19) DeepEqual/bool-8 40.3ns ± 3% 32.9ns ± 5% -18.33% (p=0.000 n=20+20) DeepEqual/[]bool-8 169ns ± 1% 124ns ± 1% -26.90% (p=0.000 n=18+19) DeepEqual/string-8 41.4ns ± 3% 33.7ns ± 5% -18.50% (p=0.000 n=19+20) DeepEqual/[]string-8 216ns ± 0% 128ns ± 1% -41.05% (p=0.000 n=19+17) DeepEqual/[]uint8#01-8 507ns ± 1% 112ns ± 2% -77.92% (p=0.000 n=20+20) DeepEqual/[][]uint8-8 613ns ± 1% 210ns ± 1% -65.76% (p=0.000 n=18+19) DeepEqual/[6]uint8-8 228ns ± 1% 162ns ± 1% -29.00% (p=0.000 n=20+19) DeepEqual/[][6]uint8-8 546ns ± 2% 269ns ± 1% -50.72% (p=0.000 n=20+19) name old alloc/op new alloc/op delta DeepEqual/int8-8 0.00B 0.00B ~ (all equal) DeepEqual/[]int8-8 2.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/int16-8 0.00B 0.00B ~ (all equal) DeepEqual/[]int16-8 4.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/int32-8 0.00B 0.00B ~ (all equal) DeepEqual/[]int32-8 8.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/int64-8 0.00B 0.00B ~ (all equal) DeepEqual/[]int64-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/int-8 0.00B 0.00B ~ (all equal) DeepEqual/[]int-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/uint8-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uint8-8 2.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/uint16-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uint16-8 4.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/uint32-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uint32-8 8.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/uint64-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uint64-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/uint-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uint-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/uintptr-8 0.00B 0.00B ~ (all equal) DeepEqual/[]uintptr-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/float32-8 0.00B 0.00B ~ (all equal) DeepEqual/[]float32-8 8.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/float64-8 0.00B 0.00B ~ (all equal) DeepEqual/[]float64-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/complex64-8 0.00B 0.00B ~ (all equal) DeepEqual/[]complex64-8 16.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/complex128-8 0.00B 0.00B ~ (all equal) DeepEqual/[]complex128-8 32.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/bool-8 0.00B 0.00B ~ (all equal) DeepEqual/[]bool-8 2.00B ± 0% 0.00B -100.00% (p=0.000 n=20+20) DeepEqual/string-8 0.00B 0.00B ~ (all equal) DeepEqual/[]string-8 32.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/[]uint8#01-8 12.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/[][]uint8-8 12.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) DeepEqual/[6]uint8-8 0.00B 0.00B ~ (all equal) DeepEqual/[][6]uint8-8 12.0B ± 0% 0.0B -100.00% (p=0.000 n=20+20) name old allocs/op new allocs/op delta DeepEqual/int8-8 0.00 0.00 ~ (all equal) DeepEqual/[]int8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/int16-8 0.00 0.00 ~ (all equal) DeepEqual/[]int16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/int32-8 0.00 0.00 ~ (all equal) DeepEqual/[]int32-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/int64-8 0.00 0.00 ~ (all equal) DeepEqual/[]int64-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/int-8 0.00 0.00 ~ (all equal) DeepEqual/[]int-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uint8-8 0.00 0.00 ~ (all equal) DeepEqual/[]uint8-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uint16-8 0.00 0.00 ~ (all equal) DeepEqual/[]uint16-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uint32-8 0.00 0.00 ~ (all equal) DeepEqual/[]uint32-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uint64-8 0.00 0.00 ~ (all equal) DeepEqual/[]uint64-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uint-8 0.00 0.00 ~ (all equal) DeepEqual/[]uint-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/uintptr-8 0.00 0.00 ~ (all equal) DeepEqual/[]uintptr-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/float32-8 0.00 0.00 ~ (all equal) DeepEqual/[]float32-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/float64-8 0.00 0.00 ~ (all equal) DeepEqual/[]float64-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/complex64-8 0.00 0.00 ~ (all equal) DeepEqual/[]complex64-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/complex128-8 0.00 0.00 ~ (all equal) DeepEqual/[]complex128-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/bool-8 0.00 0.00 ~ (all equal) DeepEqual/[]bool-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/string-8 0.00 0.00 ~ (all equal) DeepEqual/[]string-8 2.00 ± 0% 0.00 -100.00% (p=0.000 n=20+20) DeepEqual/[]uint8#01-8 12.0 ± 0% 0.0 -100.00% (p=0.000 n=20+20) DeepEqual/[][]uint8-8 12.0 ± 0% 0.0 -100.00% (p=0.000 n=20+20) DeepEqual/[6]uint8-8 0.00 0.00 ~ (all equal) DeepEqual/[][6]uint8-8 12.0 ± 0% 0.0 -100.00% (p=0.000 n=20+20) Change-Id: Ic21f0e2305f2cf5e6674c81b9ca609120b3006d9 Reviewed-on: https://go-review.googlesource.com/c/go/+/318169 Trust: Josh Bleecher Snyder <josharian@gmail.com> Trust: Joe Tsai <joetsai@digital-static.net> Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> Run-TryBot: Joe Tsai <joetsai@digital-static.net> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Joe Tsai <joetsai@digital-static.net>
This commit is contained in:
parent
ac40c9872f
commit
a0c409cbc8
@ -905,6 +905,9 @@ var deepEqualTests = []DeepEqualTest{
|
|||||||
{error(nil), error(nil), true},
|
{error(nil), error(nil), true},
|
||||||
{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true},
|
{map[int]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, true},
|
||||||
{fn1, fn2, true},
|
{fn1, fn2, true},
|
||||||
|
{[]byte{1, 2, 3}, []byte{1, 2, 3}, true},
|
||||||
|
{[]MyByte{1, 2, 3}, []MyByte{1, 2, 3}, true},
|
||||||
|
{MyBytes{1, 2, 3}, MyBytes{1, 2, 3}, true},
|
||||||
|
|
||||||
// Inequalities
|
// Inequalities
|
||||||
{1, 2, false},
|
{1, 2, false},
|
||||||
@ -950,6 +953,9 @@ var deepEqualTests = []DeepEqualTest{
|
|||||||
{&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false},
|
{&[3]interface{}{1, 2, 4}, &[3]interface{}{1, 2, "s"}, false},
|
||||||
{Basic{1, 0.5}, NotBasic{1, 0.5}, false},
|
{Basic{1, 0.5}, NotBasic{1, 0.5}, false},
|
||||||
{map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false},
|
{map[uint]string{1: "one", 2: "two"}, map[int]string{2: "two", 1: "one"}, false},
|
||||||
|
{[]byte{1, 2, 3}, []MyByte{1, 2, 3}, false},
|
||||||
|
{[]MyByte{1, 2, 3}, MyBytes{1, 2, 3}, false},
|
||||||
|
{[]byte{1, 2, 3}, MyBytes{1, 2, 3}, false},
|
||||||
|
|
||||||
// Possible loops.
|
// Possible loops.
|
||||||
{&loop1, &loop1, true},
|
{&loop1, &loop1, true},
|
||||||
@ -1049,6 +1055,82 @@ func TestDeepEqualUnexportedMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deepEqualPerfTests = []struct {
|
||||||
|
x, y interface{}
|
||||||
|
}{
|
||||||
|
{x: int8(99), y: int8(99)},
|
||||||
|
{x: []int8{99}, y: []int8{99}},
|
||||||
|
{x: int16(99), y: int16(99)},
|
||||||
|
{x: []int16{99}, y: []int16{99}},
|
||||||
|
{x: int32(99), y: int32(99)},
|
||||||
|
{x: []int32{99}, y: []int32{99}},
|
||||||
|
{x: int64(99), y: int64(99)},
|
||||||
|
{x: []int64{99}, y: []int64{99}},
|
||||||
|
{x: int(999999), y: int(999999)},
|
||||||
|
{x: []int{999999}, y: []int{999999}},
|
||||||
|
|
||||||
|
{x: uint8(99), y: uint8(99)},
|
||||||
|
{x: []uint8{99}, y: []uint8{99}},
|
||||||
|
{x: uint16(99), y: uint16(99)},
|
||||||
|
{x: []uint16{99}, y: []uint16{99}},
|
||||||
|
{x: uint32(99), y: uint32(99)},
|
||||||
|
{x: []uint32{99}, y: []uint32{99}},
|
||||||
|
{x: uint64(99), y: uint64(99)},
|
||||||
|
{x: []uint64{99}, y: []uint64{99}},
|
||||||
|
{x: uint(999999), y: uint(999999)},
|
||||||
|
{x: []uint{999999}, y: []uint{999999}},
|
||||||
|
{x: uintptr(999999), y: uintptr(999999)},
|
||||||
|
{x: []uintptr{999999}, y: []uintptr{999999}},
|
||||||
|
|
||||||
|
{x: float32(1.414), y: float32(1.414)},
|
||||||
|
{x: []float32{1.414}, y: []float32{1.414}},
|
||||||
|
{x: float64(1.414), y: float64(1.414)},
|
||||||
|
{x: []float64{1.414}, y: []float64{1.414}},
|
||||||
|
|
||||||
|
{x: complex64(1.414), y: complex64(1.414)},
|
||||||
|
{x: []complex64{1.414}, y: []complex64{1.414}},
|
||||||
|
{x: complex128(1.414), y: complex128(1.414)},
|
||||||
|
{x: []complex128{1.414}, y: []complex128{1.414}},
|
||||||
|
|
||||||
|
{x: true, y: true},
|
||||||
|
{x: []bool{true}, y: []bool{true}},
|
||||||
|
|
||||||
|
{x: "abcdef", y: "abcdef"},
|
||||||
|
{x: []string{"abcdef"}, y: []string{"abcdef"}},
|
||||||
|
|
||||||
|
{x: []byte("abcdef"), y: []byte("abcdef")},
|
||||||
|
{x: [][]byte{[]byte("abcdef")}, y: [][]byte{[]byte("abcdef")}},
|
||||||
|
|
||||||
|
{x: [6]byte{'a', 'b', 'c', 'a', 'b', 'c'}, y: [6]byte{'a', 'b', 'c', 'a', 'b', 'c'}},
|
||||||
|
{x: [][6]byte{[6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}, y: [][6]byte{[6]byte{'a', 'b', 'c', 'a', 'b', 'c'}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeepEqualAllocs(t *testing.T) {
|
||||||
|
for _, tt := range deepEqualPerfTests {
|
||||||
|
t.Run(ValueOf(tt.x).Type().String(), func(t *testing.T) {
|
||||||
|
got := testing.AllocsPerRun(100, func() {
|
||||||
|
if !DeepEqual(tt.x, tt.y) {
|
||||||
|
t.Errorf("DeepEqual(%v, %v)=false", tt.x, tt.y)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if int(got) != 0 {
|
||||||
|
t.Errorf("DeepEqual(%v, %v) allocated %d times", tt.x, tt.y, int(got))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDeepEqual(b *testing.B) {
|
||||||
|
for _, bb := range deepEqualPerfTests {
|
||||||
|
b.Run(ValueOf(bb.x).Type().String(), func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
sink = DeepEqual(bb.x, bb.y)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func check2ndField(x interface{}, offs uintptr, t *testing.T) {
|
func check2ndField(x interface{}, offs uintptr, t *testing.T) {
|
||||||
s := ValueOf(x)
|
s := ValueOf(x)
|
||||||
f := s.Type().Field(1)
|
f := s.Type().Field(1)
|
||||||
|
@ -6,7 +6,10 @@
|
|||||||
|
|
||||||
package reflect
|
package reflect
|
||||||
|
|
||||||
import "unsafe"
|
import (
|
||||||
|
"internal/bytealg"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
// During deepValueEqual, must keep track of checks that are
|
// During deepValueEqual, must keep track of checks that are
|
||||||
// in progress. The comparison algorithm assumes that all
|
// in progress. The comparison algorithm assumes that all
|
||||||
@ -102,6 +105,10 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {
|
|||||||
if v1.Pointer() == v2.Pointer() {
|
if v1.Pointer() == v2.Pointer() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// Special case for []byte, which is common.
|
||||||
|
if v1.Type().Elem().Kind() == Uint8 {
|
||||||
|
return bytealg.Equal(v1.Bytes(), v2.Bytes())
|
||||||
|
}
|
||||||
for i := 0; i < v1.Len(); i++ {
|
for i := 0; i < v1.Len(); i++ {
|
||||||
if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
|
if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
|
||||||
return false
|
return false
|
||||||
@ -149,6 +156,18 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {
|
|||||||
}
|
}
|
||||||
// Can't do better than this:
|
// Can't do better than this:
|
||||||
return false
|
return false
|
||||||
|
case Int, Int8, Int16, Int32, Int64:
|
||||||
|
return v1.Int() == v2.Int()
|
||||||
|
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
|
||||||
|
return v1.Uint() == v2.Uint()
|
||||||
|
case String:
|
||||||
|
return v1.String() == v2.String()
|
||||||
|
case Bool:
|
||||||
|
return v1.Bool() == v2.Bool()
|
||||||
|
case Float32, Float64:
|
||||||
|
return v1.Float() == v2.Float()
|
||||||
|
case Complex64, Complex128:
|
||||||
|
return v1.Complex() == v2.Complex()
|
||||||
default:
|
default:
|
||||||
// Normal equality suffices
|
// Normal equality suffices
|
||||||
return valueInterface(v1, false) == valueInterface(v2, false)
|
return valueInterface(v1, false) == valueInterface(v2, false)
|
||||||
|
Loading…
Reference in New Issue
Block a user