mirror of
https://github.com/golang/go.git
synced 2024-09-21 18:38:37 +00:00
reflect: optimize Value.IsZero
If a struct or array is comparable, then we can leverage rtype.equal, which is almost always faster than what Go reflection can achieve. As a secondary optimization, pre-compute Value.Len and Value.NumField outside of the loop conditional. Performance: name old time/op new time/op delta IsZero/ArrayComparable 136ns ± 4% 16ns ± 1% -88.28% (p=0.008 n=5+5) IsZero/ArrayIncomparable 197ns ±10% 123ns ± 1% -37.74% (p=0.008 n=5+5) IsZero/StructComparable 26.4ns ± 0% 9.6ns ± 1% -63.68% (p=0.016 n=4+5) IsZero/StructIncomparable 43.5ns ± 1% 27.8ns ± 1% -36.21% (p=0.008 n=5+5) The incomparable types gain a performance boost since they are generally constructed from nested comparable types. Change-Id: If2c1929f8bb1b5b19306ef0c69f3c95a27d4b60d Reviewed-on: https://go-review.googlesource.com/c/go/+/411478 Reviewed-by: Dan Kortschak <dan@kortschak.io> Run-TryBot: Joseph Tsai <joetsai@digital-static.net> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
1a8dfadbfe
commit
1ab6b790be
@ -47,6 +47,8 @@ type T struct {
|
||||
d *int
|
||||
}
|
||||
|
||||
var _ = T{} == T{} // tests depend on T being comparable
|
||||
|
||||
type pair struct {
|
||||
i any
|
||||
s string
|
||||
@ -1364,9 +1366,14 @@ func TestIsZero(t *testing.T) {
|
||||
{uintptr(128), false},
|
||||
// Array
|
||||
{Zero(TypeOf([5]string{})).Interface(), true},
|
||||
{[5]string{"", "", "", "", ""}, true},
|
||||
{[5]string{}, true},
|
||||
{[5]string{"", "", "", "a", ""}, false},
|
||||
{[5]string{}, true}, // comparable array
|
||||
{[5]string{"", "", "", "a", ""}, false}, // comparable array
|
||||
{[1]*int{}, true}, // direct pointer array
|
||||
{[1]*int{new(int)}, false}, // direct pointer array
|
||||
{[3][]int{}, true}, // incomparable array
|
||||
{[3][]int{{1}}, false}, // incomparable array
|
||||
{[1 << 12]byte{}, true},
|
||||
{[1 << 12]byte{1}, false},
|
||||
// Chan
|
||||
{(chan string)(nil), true},
|
||||
{make(chan string), false},
|
||||
@ -1393,8 +1400,12 @@ func TestIsZero(t *testing.T) {
|
||||
{"", true},
|
||||
{"not-zero", false},
|
||||
// Structs
|
||||
{T{}, true},
|
||||
{T{123, 456.75, "hello", &_i}, false},
|
||||
{T{}, true}, // comparable struct
|
||||
{T{123, 456.75, "hello", &_i}, false}, // comparable struct
|
||||
{struct{ p *int }{}, true}, // direct pointer struct
|
||||
{struct{ p *int }{new(int)}, false}, // direct pointer struct
|
||||
{struct{ s []int }{}, true}, // incomparable struct
|
||||
{struct{ s []int }{[]int{1}}, false}, // incomparable struct
|
||||
// UnsafePointer
|
||||
{(unsafe.Pointer)(nil), true},
|
||||
{(unsafe.Pointer)(new(int)), false},
|
||||
@ -1426,6 +1437,25 @@ func TestIsZero(t *testing.T) {
|
||||
}()
|
||||
}
|
||||
|
||||
func BenchmarkIsZero(b *testing.B) {
|
||||
source := ValueOf(struct {
|
||||
ArrayComparable [4]T
|
||||
ArrayIncomparable [4]_Complex
|
||||
StructComparable T
|
||||
StructIncomparable _Complex
|
||||
}{})
|
||||
|
||||
for i := 0; i < source.NumField(); i++ {
|
||||
name := source.Type().Field(i).Name
|
||||
value := source.Field(i)
|
||||
b.Run(name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = value.IsZero()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfaceExtraction(t *testing.T) {
|
||||
var s struct {
|
||||
W io.Writer
|
||||
|
@ -1579,7 +1579,16 @@ func (v Value) IsZero() bool {
|
||||
c := v.Complex()
|
||||
return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0
|
||||
case Array:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
// If the type is comparable, then compare directly with zero.
|
||||
if v.typ.equal != nil && v.typ.size <= maxZero {
|
||||
if v.flag&flagIndir == 0 {
|
||||
return v.ptr == nil
|
||||
}
|
||||
return v.typ.equal(v.ptr, unsafe.Pointer(&zeroVal[0]))
|
||||
}
|
||||
|
||||
n := v.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
if !v.Index(i).IsZero() {
|
||||
return false
|
||||
}
|
||||
@ -1590,7 +1599,16 @@ func (v Value) IsZero() bool {
|
||||
case String:
|
||||
return v.Len() == 0
|
||||
case Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
// If the type is comparable, then compare directly with zero.
|
||||
if v.typ.equal != nil && v.typ.size <= maxZero {
|
||||
if v.flag&flagIndir == 0 {
|
||||
return v.ptr == nil
|
||||
}
|
||||
return v.typ.equal(v.ptr, unsafe.Pointer(&zeroVal[0]))
|
||||
}
|
||||
|
||||
n := v.NumField()
|
||||
for i := 0; i < n; i++ {
|
||||
if !v.Field(i).IsZero() {
|
||||
return false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user