]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: add Value.SetZero
authorJoe Tsai <joetsai@digital-static.net>
Fri, 10 Jun 2022 01:20:47 +0000 (18:20 -0700)
committerGopher Robot <gobot@golang.org>
Fri, 26 Aug 2022 17:15:08 +0000 (17:15 +0000)
The v.SetZero method is a faster equivalent of v.Set(Zero(v.Type())).

Performance:

                     Direct         CachedZero     NewZero
SetZero/Bool         2.20ns ± 0%    8.97ns ± 5%    11.4ns ± 1%
SetZero/Int          3.08ns ± 1%    9.06ns ± 1%    11.5ns ± 0%
SetZero/Uint         2.88ns ± 1%    9.04ns ± 1%    11.7ns ± 5%
SetZero/Float        2.65ns ± 2%    9.05ns ± 1%    11.5ns ± 1%
SetZero/Complex      2.68ns ± 3%    9.31ns ± 1%    11.7ns ± 1%
SetZero/Array        6.69ns ± 4%    9.32ns ± 1%    11.8ns ± 1%
SetZero/Chan         3.31ns ± 1%    6.19ns ± 1%    8.20ns ± 1%
SetZero/Func         3.32ns ± 1%    6.20ns ± 0%    8.24ns ± 1%
SetZero/Interface    2.66ns ± 1%    9.31ns ± 1%    11.8ns ± 1%
SetZero/Map          3.31ns ± 1%    6.21ns ± 2%    8.19ns ± 1%
SetZero/Pointer      3.30ns ± 1%    6.22ns ± 1%    8.17ns ± 1%
SetZero/Slice        2.90ns ± 4%    9.13ns ± 1%    11.6ns ± 1%
SetZero/String       3.11ns ± 1%    9.30ns ± 1%    11.8ns ± 2%
SetZero/Struct       6.37ns ± 1%    9.18ns ± 4%    11.5ns ± 1%

where:

* Direct is measuring Value.SetZero
* CachedZero is measuring Value.Set with a cached Zero value
* NewZero is measuring Value.Set with a new Zero value

Fixes #52376

Change-Id: I793ca723aa97627824c5f5b356b2da30c8e46d71
Reviewed-on: https://go-review.googlesource.com/c/go/+/411476
Auto-Submit: Joseph Tsai <joetsai@digital-static.net>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Joseph Tsai <joetsai@digital-static.net>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Reviewed-by: Dan Kortschak <dan@kortschak.io>
api/next/52376.txt [new file with mode: 0644]
src/reflect/all_test.go
src/reflect/value.go

diff --git a/api/next/52376.txt b/api/next/52376.txt
new file mode 100644 (file)
index 0000000..9e6b162
--- /dev/null
@@ -0,0 +1 @@
+pkg reflect, method (Value) SetZero() #52376
index 37e01e0be4db9b4dd64956b34c578a882e1d5794..fe624074121512b4969a631623bf735f79964e52 100644 (file)
@@ -1374,6 +1374,8 @@ func TestIsZero(t *testing.T) {
                {[3][]int{{1}}, false},                  // incomparable array
                {[1 << 12]byte{}, true},
                {[1 << 12]byte{1}, false},
+               {[3]Value{}, true},
+               {[3]Value{{}, ValueOf(0), {}}, false},
                // Chan
                {(chan string)(nil), true},
                {make(chan string), false},
@@ -1406,6 +1408,8 @@ func TestIsZero(t *testing.T) {
                {struct{ p *int }{new(int)}, false},   // direct pointer struct
                {struct{ s []int }{}, true},           // incomparable struct
                {struct{ s []int }{[]int{1}}, false},  // incomparable struct
+               {struct{ Value }{}, true},
+               {struct{ Value }{ValueOf(0)}, false},
                // UnsafePointer
                {(unsafe.Pointer)(nil), true},
                {(unsafe.Pointer)(new(int)), false},
@@ -1425,6 +1429,13 @@ func TestIsZero(t *testing.T) {
                if !Zero(TypeOf(tt.x)).IsZero() {
                        t.Errorf("%d: IsZero(Zero(TypeOf((%s)(%+v)))) is false", i, x.Kind(), tt.x)
                }
+
+               p := New(x.Type()).Elem()
+               p.Set(x)
+               p.SetZero()
+               if !p.IsZero() {
+                       t.Errorf("%d: IsZero((%s)(%+v)) is true after SetZero", i, p.Kind(), tt.x)
+               }
        }
 
        func() {
@@ -1456,6 +1467,46 @@ func BenchmarkIsZero(b *testing.B) {
        }
 }
 
+func BenchmarkSetZero(b *testing.B) {
+       source := ValueOf(new(struct {
+               Bool      bool
+               Int       int64
+               Uint      uint64
+               Float     float64
+               Complex   complex128
+               Array     [4]Value
+               Chan      chan Value
+               Func      func() Value
+               Interface interface{ String() string }
+               Map       map[string]Value
+               Pointer   *Value
+               Slice     []Value
+               String    string
+               Struct    Value
+       })).Elem()
+
+       for i := 0; i < source.NumField(); i++ {
+               name := source.Type().Field(i).Name
+               value := source.Field(i)
+               zero := Zero(value.Type())
+               b.Run(name+"/Direct", func(b *testing.B) {
+                       for i := 0; i < b.N; i++ {
+                               value.SetZero()
+                       }
+               })
+               b.Run(name+"/CachedZero", func(b *testing.B) {
+                       for i := 0; i < b.N; i++ {
+                               value.Set(zero)
+                       }
+               })
+               b.Run(name+"/NewZero", func(b *testing.B) {
+                       for i := 0; i < b.N; i++ {
+                               value.Set(Zero(value.Type()))
+                       }
+               })
+       }
+}
+
 func TestInterfaceExtraction(t *testing.T) {
        var s struct {
                W io.Writer
index 95bf4682aa6fa9c72f492b3daa28b0e452f13c52..6ab6cc8b232a75d66564b8bce973ef4eeaa68185 100644 (file)
@@ -1615,12 +1615,66 @@ func (v Value) IsZero() bool {
                }
                return true
        default:
-               // This should never happens, but will act as a safeguard for
-               // later, as a default value doesn't makes sense here.
+               // This should never happen, but will act as a safeguard for later,
+               // as a default value doesn't makes sense here.
                panic(&ValueError{"reflect.Value.IsZero", v.Kind()})
        }
 }
 
+// SetZero sets v to be the zero value of v's type.
+// It panics if CanSet returns false.
+func (v Value) SetZero() {
+       v.mustBeAssignable()
+       switch v.kind() {
+       case Bool:
+               *(*bool)(v.ptr) = false
+       case Int:
+               *(*int)(v.ptr) = 0
+       case Int8:
+               *(*int8)(v.ptr) = 0
+       case Int16:
+               *(*int16)(v.ptr) = 0
+       case Int32:
+               *(*int32)(v.ptr) = 0
+       case Int64:
+               *(*int64)(v.ptr) = 0
+       case Uint:
+               *(*uint)(v.ptr) = 0
+       case Uint8:
+               *(*uint8)(v.ptr) = 0
+       case Uint16:
+               *(*uint16)(v.ptr) = 0
+       case Uint32:
+               *(*uint32)(v.ptr) = 0
+       case Uint64:
+               *(*uint64)(v.ptr) = 0
+       case Uintptr:
+               *(*uintptr)(v.ptr) = 0
+       case Float32:
+               *(*float32)(v.ptr) = 0
+       case Float64:
+               *(*float64)(v.ptr) = 0
+       case Complex64:
+               *(*complex64)(v.ptr) = 0
+       case Complex128:
+               *(*complex128)(v.ptr) = 0
+       case String:
+               *(*string)(v.ptr) = ""
+       case Slice:
+               *(*unsafeheader.Slice)(v.ptr) = unsafeheader.Slice{}
+       case Interface:
+               *(*[2]unsafe.Pointer)(v.ptr) = [2]unsafe.Pointer{}
+       case Chan, Func, Map, Pointer, UnsafePointer:
+               *(*unsafe.Pointer)(v.ptr) = nil
+       case Array, Struct:
+               typedmemclr(v.typ, v.ptr)
+       default:
+               // This should never happen, but will act as a safeguard for later,
+               // as a default value doesn't makes sense here.
+               panic(&ValueError{"reflect.Value.SetZero", v.Kind()})
+       }
+}
+
 // Kind returns v's Kind.
 // If v is the zero Value (IsValid returns false), Kind returns Invalid.
 func (v Value) Kind() Kind {