mirror of
https://github.com/golang/go
synced 2024-11-16 17:04:41 -07:00
reflect: optimize Value.IsZero for array types
For some types where the zero value is a value where all bits of this type are 0
optimize it
goos: windows
goarch: amd64
pkg: reflect
cpu: AMD Ryzen 7 7840HS w/ Radeon 780M Graphics
│ std.txt │ new.txt │
│ sec/op │ sec/op vs base │
IsZero/ArrayComparable-16 8.483n ± 0% 8.470n ± 2% ~ (p=0.542 n=10)
IsZero/ArrayIncomparable-16 88.13n ± 1% 87.34n ± 2% ~ (p=0.110 n=10)
IsZero/StructComparable-16 4.050n ± 2% 4.011n ± 1% ~ (p=0.093 n=10)
IsZero/StructIncomparable-16 19.93n ± 1% 19.81n ± 1% ~ (p=0.493 n=10)
IsZero/ArrayInt_4-16 4.445n ± 2% 4.478n ± 2% ~ (p=0.306 n=10)
IsZero/ArrayInt_1024-16 3381.5n ± 3% 140.8n ± 1% -95.84% (p=0.000 n=10)
IsZero/ArrayInt_1024_NoZero-16 1760.50n ± 3% 72.17n ± 1% -95.90% (p=0.000 n=10)
IsZero/Struct4Int-16 4.495n ± 3% 4.478n ± 1% ~ (p=0.579 n=10)
IsZero/ArrayStruct4Int_1024-16 1404.0n ± 3% 140.5n ± 0% -90.00% (p=0.000 n=10)
IsZero/ArrayChanInt_1024-16 3437.0n ± 6% 140.5n ± 1% -95.91% (p=0.000 n=10)
geomean 89.94n 27.38n -69.56%
Change-Id: I835231a79b9cd89686d44c5b8c2fbe629ccd98ba
GitHub-Last-Rev: 3abe118a10
GitHub-Pull-Request: golang/go#63661
Reviewed-on: https://go-review.googlesource.com/c/go/+/536855
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
This commit is contained in:
parent
b7cdc277e1
commit
12051f7d95
@ -1454,6 +1454,8 @@ func TestIsZero(t *testing.T) {
|
|||||||
{[3][]int{{1}}, false}, // incomparable array
|
{[3][]int{{1}}, false}, // incomparable array
|
||||||
{[1 << 12]byte{}, true},
|
{[1 << 12]byte{}, true},
|
||||||
{[1 << 12]byte{1}, false},
|
{[1 << 12]byte{1}, false},
|
||||||
|
{[1]struct{ p *int }{}, true},
|
||||||
|
{[1]struct{ p *int }{{new(int)}}, false},
|
||||||
{[3]Value{}, true},
|
{[3]Value{}, true},
|
||||||
{[3]Value{{}, ValueOf(0), {}}, false},
|
{[3]Value{{}, ValueOf(0), {}}, false},
|
||||||
// Chan
|
// Chan
|
||||||
@ -1536,6 +1538,15 @@ func TestIsZero(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInternalIsZero(t *testing.T) {
|
||||||
|
b := make([]byte, 512)
|
||||||
|
for a := 0; a < 8; a++ {
|
||||||
|
for i := 256 + 7; i <= 512-a; i++ {
|
||||||
|
InternalIsZero(b[a : a+i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestInterfaceExtraction(t *testing.T) {
|
func TestInterfaceExtraction(t *testing.T) {
|
||||||
var s struct {
|
var s struct {
|
||||||
W io.Writer
|
W io.Writer
|
||||||
|
@ -120,12 +120,26 @@ func BenchmarkMapsDeepEqual(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkIsZero(b *testing.B) {
|
func BenchmarkIsZero(b *testing.B) {
|
||||||
source := ValueOf(struct {
|
type Int4 struct {
|
||||||
ArrayComparable [4]T
|
a, b, c, d int
|
||||||
ArrayIncomparable [4]_Complex
|
}
|
||||||
StructComparable T
|
type Int1024 struct {
|
||||||
StructIncomparable _Complex
|
a [1024]int
|
||||||
}{})
|
}
|
||||||
|
s := struct {
|
||||||
|
ArrayComparable [4]T
|
||||||
|
ArrayIncomparable [4]_Complex
|
||||||
|
StructComparable T
|
||||||
|
StructIncomparable _Complex
|
||||||
|
ArrayInt_4 [4]int
|
||||||
|
ArrayInt_1024 [1024]int
|
||||||
|
ArrayInt_1024_NoZero [1024]int
|
||||||
|
Struct4Int Int4
|
||||||
|
ArrayStruct4Int_1024 [256]Int4
|
||||||
|
ArrayChanInt_1024 [1024]chan int
|
||||||
|
}{}
|
||||||
|
s.ArrayInt_1024_NoZero[512] = 1
|
||||||
|
source := ValueOf(s)
|
||||||
|
|
||||||
for i := 0; i < source.NumField(); i++ {
|
for i := 0; i < source.NumField(); i++ {
|
||||||
name := source.Type().Field(i).Name
|
name := source.Type().Field(i).Name
|
||||||
|
@ -164,3 +164,5 @@ func SetArgRegs(ints, floats int, floatSize uintptr) (oldInts, oldFloats int, ol
|
|||||||
}
|
}
|
||||||
|
|
||||||
var MethodValueCallCodePtr = methodValueCallCodePtr
|
var MethodValueCallCodePtr = methodValueCallCodePtr
|
||||||
|
|
||||||
|
var InternalIsZero = isZero
|
||||||
|
@ -1598,18 +1598,24 @@ func (v Value) IsZero() bool {
|
|||||||
case Complex64, Complex128:
|
case Complex64, Complex128:
|
||||||
return v.Complex() == 0
|
return v.Complex() == 0
|
||||||
case Array:
|
case Array:
|
||||||
|
array := (*abi.ArrayType)(unsafe.Pointer(v.typ()))
|
||||||
|
// Avoid performance degradation of small benchmarks.
|
||||||
// If the type is comparable, then compare directly with zero.
|
// If the type is comparable, then compare directly with zero.
|
||||||
if v.typ().Equal != nil && v.typ().Size() <= maxZero {
|
if array.Equal != nil && array.Size() <= maxZero {
|
||||||
if v.flag&flagIndir == 0 {
|
if v.flag&flagIndir == 0 {
|
||||||
return v.ptr == nil
|
return v.ptr == nil
|
||||||
}
|
}
|
||||||
// v.ptr doesn't escape, as Equal functions are compiler generated
|
// v.ptr doesn't escape, as Equal functions are compiler generated
|
||||||
// and never escape. The escape analysis doesn't know, as it is a
|
// and never escape. The escape analysis doesn't know, as it is a
|
||||||
// function pointer call.
|
// function pointer call.
|
||||||
return v.typ().Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0]))
|
return array.Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0]))
|
||||||
}
|
}
|
||||||
|
if array.TFlag&abi.TFlagRegularMemory != 0 {
|
||||||
n := v.Len()
|
// For some types where the zero value is a value where all bits of this type are 0
|
||||||
|
// optimize it.
|
||||||
|
return isZero(unsafe.Slice(((*byte)(v.ptr)), array.Size()))
|
||||||
|
}
|
||||||
|
n := int(array.Len)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
if !v.Index(i).IsZero() {
|
if !v.Index(i).IsZero() {
|
||||||
return false
|
return false
|
||||||
@ -1644,6 +1650,37 @@ func (v Value) IsZero() bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isZero must have len(b)>256+7 to ensure at
|
||||||
|
// least one 8-byte aligned [256]byte,
|
||||||
|
// otherwise the access will be out of bounds.
|
||||||
|
// For all zeros, performance is not as good as
|
||||||
|
// return bytealg.Count(b, byte(0)) == len(b)
|
||||||
|
func isZero(b []byte) bool {
|
||||||
|
const n = 32
|
||||||
|
const bit = n * 8
|
||||||
|
// Align memory addresses to 8 bytes
|
||||||
|
for uintptr(unsafe.Pointer(&b[0]))%8 != 0 {
|
||||||
|
if b[0] != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
b = b[1:]
|
||||||
|
}
|
||||||
|
for len(b)%bit != 0 {
|
||||||
|
if b[len(b)-1] != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
b = b[:len(b)-1]
|
||||||
|
}
|
||||||
|
w := unsafe.Slice((*uint64)(unsafe.Pointer(&b[0])), len(b)/8)
|
||||||
|
for len(w) >= n {
|
||||||
|
if w[0] != 0 || w[1] != 0 || w[2] != 0 || w[3] != 0 || w[4] != 0 || w[5] != 0 || w[6] != 0 || w[7] != 0 || w[8] != 0 || w[9] != 0 || w[10] != 0 || w[11] != 0 || w[12] != 0 || w[13] != 0 || w[14] != 0 || w[15] != 0 || w[16] != 0 || w[17] != 0 || w[18] != 0 || w[19] != 0 || w[20] != 0 || w[21] != 0 || w[22] != 0 || w[23] != 0 || w[24] != 0 || w[25] != 0 || w[26] != 0 || w[27] != 0 || w[28] != 0 || w[29] != 0 || w[30] != 0 || w[31] != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
w = w[n:]
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// SetZero sets v to be the zero value of v's type.
|
// SetZero sets v to be the zero value of v's type.
|
||||||
// It panics if [Value.CanSet] returns false.
|
// It panics if [Value.CanSet] returns false.
|
||||||
func (v Value) SetZero() {
|
func (v Value) SetZero() {
|
||||||
|
Loading…
Reference in New Issue
Block a user