]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: optimize Value.IsZero for struct types
authorqiulaidongfeng <2645477756@qq.com>
Sun, 19 Nov 2023 11:22:05 +0000 (11:22 +0000)
committerGopher Robot <gobot@golang.org>
Sun, 19 Nov 2023 17:09:03 +0000 (17:09 +0000)
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 <drchase@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
src/reflect/all_test.go
src/reflect/benchmark_test.go
src/reflect/value.go

index 71969106e495004ac1024f5e27c9dc24c755c2ee..a28f2a4bed14fe07a1e0d2926eae14b1364e77f3 100644 (file)
@@ -1500,6 +1500,12 @@ func TestIsZero(t *testing.T) {
                {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 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++ {
+               for i := 1; i <= 512-a; i++ {
                        InternalIsZero(b[a : a+i])
                }
        }
index 4aa47669a27507309792915fbe8d18f1202cf6ac..2e701b062edf6ff5bc48730959feacae3884e347 100644 (file)
@@ -126,6 +126,9 @@ func BenchmarkIsZero(b *testing.B) {
        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 @@ func BenchmarkIsZero(b *testing.B) {
                Struct4Int           Int4
                ArrayStruct4Int_1024 [256]Int4
                ArrayChanInt_1024    [1024]chan int
+               StructInt_512        Int512
        }{}
        s.ArrayInt_1024_NoZero[512] = 1
        source := ValueOf(s)
index 5bfdb55fd92ac8c477dbf00baf74a2c1fc6f0268..2bd41f37fdfccac083534230a00e43dbf2b3385e 100644 (file)
@@ -1627,13 +1627,19 @@ func (v Value) IsZero() bool {
        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 @@ 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
+// 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