[go] reflect: optimize Value.IsZero for struct types

23 views
Skip to first unread message

Gopher Robot (Gerrit)

unread,
Nov 19, 2023, 12:09:08 PM11/19/23
to Gerrit Bot, qiulaidongfeng, goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, David Chase, Keith Randall, Jes Cok, Cherry Mui, Russ Cox, Ian Lance Taylor, golang-co...@googlegroups.com

Gopher Robot submitted this change.

View Change

Approvals: Keith Randall: Looks good to me, approved; Automatically submit change Gopher Robot: TryBots succeeded David Chase: Looks good to me, but someone else must approve Cherry Mui: Looks good to me, but someone else must approve
reflect: optimize Value.IsZero for struct 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
│ old.txt │ new.txt │
│ sec/op │ sec/op vs base │
IsZero/StructInt_512-16 109.75n ± 0% 72.61n ± 1% -33.84% (p=0.000 n=12)

Change-Id: I56de8b95f4d4482068960d6f38938763fa1caa90
GitHub-Last-Rev: c143f0cd7616cb3be52c59879f748e49a3c5cbf1
GitHub-Pull-Request: golang/go#64220
Reviewed-on: https://go-review.googlesource.com/c/go/+/543355
Reviewed-by: David Chase <drc...@google.com>
Reviewed-by: Cherry Mui <cher...@google.com>
TryBot-Result: Gopher Robot <go...@golang.org>
Auto-Submit: Keith Randall <k...@golang.org>
Reviewed-by: Keith Randall <k...@golang.org>
---
M src/reflect/all_test.go
M src/reflect/benchmark_test.go
M src/reflect/value.go
3 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 7196910..a28f2a4 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -1500,6 +1500,12 @@
{setField(struct{ _, a, _ func() }{}, 0*unsafe.Sizeof((func())(nil)), func() {}), true},
{setField(struct{ _, a, _ func() }{}, 1*unsafe.Sizeof((func())(nil)), func() {}), false},
{setField(struct{ _, a, _ func() }{}, 2*unsafe.Sizeof((func())(nil)), func() {}), true},
+ {struct{ a [256]S }{}, true},
+ {struct{ a [256]S }{a: [256]S{2: {i1: 1}}}, false},
+ {struct{ a [256]float32 }{}, true},
+ {struct{ a [256]float32 }{a: [256]float32{2: 1.0}}, false},
+ {struct{ _, a [256]S }{}, true},
+ {setField(struct{ _, a [256]S }{}, 0*unsafe.Sizeof(int64(0)), int64(1)), true},
// UnsafePointer
{(unsafe.Pointer)(nil), true},
{(unsafe.Pointer)(new(int)), false},
@@ -1541,7 +1547,7 @@
func TestInternalIsZero(t *testing.T) {
b := make([]byte, 512)
for a := 0; a < 8; a++ {
- for i := 256 + 7; i <= 512-a; i++ {
+ for i := 1; i <= 512-a; i++ {
InternalIsZero(b[a : a+i])
}
}
diff --git a/src/reflect/benchmark_test.go b/src/reflect/benchmark_test.go
index 4aa4766..2e701b0 100644
--- a/src/reflect/benchmark_test.go
+++ b/src/reflect/benchmark_test.go
@@ -126,6 +126,9 @@
type Int1024 struct {
a [1024]int
}
+ type Int512 struct {
+ a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16 [16]S
+ }
s := struct {
ArrayComparable [4]T
ArrayIncomparable [4]_Complex
@@ -137,6 +140,7 @@
Struct4Int Int4
ArrayStruct4Int_1024 [256]Int4
ArrayChanInt_1024 [1024]chan int
+ StructInt_512 Int512
}{}
s.ArrayInt_1024_NoZero[512] = 1
source := ValueOf(s)
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 5bfdb55..2bd41f3 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -1627,13 +1627,19 @@
case String:
return v.Len() == 0
case Struct:
+ if v.flag&flagIndir == 0 {
+ return v.ptr == nil
+ }
+ typ := (*abi.StructType)(unsafe.Pointer(v.typ()))
// 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
- }
+ if typ.Equal != nil && typ.Size() <= maxZero {
// See noescape justification above.
- return v.typ().Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0]))
+ return typ.Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0]))
+ }
+ if typ.TFlag&abi.TFlagRegularMemory != 0 {
+ // 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)), typ.Size()))
}

n := v.NumField()
@@ -1650,28 +1656,39 @@
}
}

-// 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
+// isZero For all zeros, performance is not as good as
// return bytealg.Count(b, byte(0)) == len(b)
func isZero(b []byte) bool {
+ if len(b) == 0 {
+ return true
+ }
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:]
+ if len(b) == 0 {
+ return true
+ }
}
- for len(b)%bit != 0 {
+ for len(b)%8 != 0 {
if b[len(b)-1] != 0 {
return false
}
b = b[:len(b)-1]
}
+ if len(b) == 0 {
+ return true
+ }
w := unsafe.Slice((*uint64)(unsafe.Pointer(&b[0])), len(b)/8)
+ for len(w)%n != 0 {
+ if w[0] != 0 {
+ return false
+ }
+ w = w[1:]
+ }
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

To view, visit change 543355. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: merged
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I56de8b95f4d4482068960d6f38938763fa1caa90
Gerrit-Change-Number: 543355
Gerrit-PatchSet: 10
Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
Gerrit-Reviewer: Cherry Mui <cher...@google.com>
Gerrit-Reviewer: David Chase <drc...@google.com>
Gerrit-Reviewer: Gopher Robot <go...@golang.org>
Gerrit-Reviewer: Ian Lance Taylor <ia...@golang.org>
Gerrit-Reviewer: Keith Randall <k...@golang.org>
Gerrit-Reviewer: Russ Cox <r...@golang.org>
Gerrit-CC: Jes Cok <xigua...@gmail.com>
Gerrit-CC: qiulaidongfeng <26454...@qq.com>
Reply all
Reply to author
Forward
0 new messages