]> Cypherpunks.ru repositories - gostls13.git/commitdiff
reflect: optimize Value.IsZero for array types
authorqiulaidongfeng <2645477756@qq.com>
Tue, 7 Nov 2023 13:39:17 +0000 (13:39 +0000)
committerKeith Randall <khr@golang.org>
Wed, 15 Nov 2023 04:10:05 +0000 (04:10 +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
                               │    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: 3abe118a108faf0070b56ba9098871746daa1ac1
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>
src/reflect/all_test.go
src/reflect/benchmark_test.go
src/reflect/export_test.go
src/reflect/value.go

index 961595aa3232f30b6870c66bed701f0b68f47c7a..bece56f7e73b06c670442b811ad8819cf881477f 100644 (file)
@@ -1454,6 +1454,8 @@ func TestIsZero(t *testing.T) {
                {[3][]int{{1}}, false},                  // incomparable array
                {[1 << 12]byte{}, true},
                {[1 << 12]byte{1}, false},
+               {[1]struct{ p *int }{}, true},
+               {[1]struct{ p *int }{{new(int)}}, false},
                {[3]Value{}, true},
                {[3]Value{{}, ValueOf(0), {}}, false},
                // 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) {
        var s struct {
                W io.Writer
index b499ad71220326f3f8784e01721e73b01ed9d649..4aa47669a27507309792915fbe8d18f1202cf6ac 100644 (file)
@@ -120,12 +120,26 @@ func BenchmarkMapsDeepEqual(b *testing.B) {
 }
 
 func BenchmarkIsZero(b *testing.B) {
-       source := ValueOf(struct {
-               ArrayComparable    [4]T
-               ArrayIncomparable  [4]_Complex
-               StructComparable   T
-               StructIncomparable _Complex
-       }{})
+       type Int4 struct {
+               a, b, c, d int
+       }
+       type Int1024 struct {
+               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++ {
                name := source.Type().Field(i).Name
index 8a6dc44765708e37475490560cbe6e801ae882eb..1648eb362490ce090d0f694063fc9ab64ec8adad 100644 (file)
@@ -164,3 +164,5 @@ func SetArgRegs(ints, floats int, floatSize uintptr) (oldInts, oldFloats int, ol
 }
 
 var MethodValueCallCodePtr = methodValueCallCodePtr
+
+var InternalIsZero = isZero
index ec75fcced9fe7a3563f40ae7ceeecaa5166ff972..0452b51d7b1773044aa5b7f200d724e1d5f810f2 100644 (file)
@@ -1598,18 +1598,24 @@ func (v Value) IsZero() bool {
        case Complex64, Complex128:
                return v.Complex() == 0
        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 v.typ().Equal != nil && v.typ().Size() <= maxZero {
+               if array.Equal != nil && array.Size() <= maxZero {
                        if v.flag&flagIndir == 0 {
                                return v.ptr == nil
                        }
                        // v.ptr doesn't escape, as Equal functions are compiler generated
                        // and never escape. The escape analysis doesn't know, as it is a
                        // function pointer call.
-                       return v.typ().Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0]))
+                       return array.Equal(noescape(v.ptr), unsafe.Pointer(&zeroVal[0]))
                }
-
-               n := v.Len()
+               if array.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)), array.Size()))
+               }
+               n := int(array.Len)
                for i := 0; i < n; i++ {
                        if !v.Index(i).IsZero() {
                                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.
 // It panics if [Value.CanSet] returns false.
 func (v Value) SetZero() {