mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
reflect: allocate memory in TypeAssert[I] only when the assertion succeeds
goos: linux
goarch: amd64
pkg: reflect
cpu: 11th Gen Intel(R) Core(TM) i7-1185G7 @ 3.00GHz
│ /tmp/before │ /tmp/after │
│ sec/op │ sec/op vs base │
TypeAssert/TypeAssert[int](int)-8 2.599n ± 1% 2.558n ± 0% -1.56% (p=0.000 n=30)
TypeAssert/TypeAssert[uint8](int)-8 2.506n ± 1% 2.579n ± 2% +2.93% (p=0.008 n=30)
TypeAssert/TypeAssert[fmt.Stringer](reflect_test.testTypeWithMethod)-8 7.449n ± 2% 7.776n ± 2% +4.39% (p=0.000 n=30)
TypeAssert/TypeAssert[fmt.Stringer](*reflect_test.testTypeWithMethod)-8 7.220n ± 2% 7.439n ± 1% +3.04% (p=0.000 n=30)
TypeAssert/TypeAssert[interface_{}](int)-8 4.015n ± 1% 4.207n ± 1% +4.79% (p=0.000 n=30)
TypeAssert/TypeAssert[interface_{}](reflect_test.testTypeWithMethod)-8 4.003n ± 1% 4.190n ± 2% +4.66% (p=0.000 n=30)
TypeAssert/TypeAssert[time.Time](time.Time)-8 2.933n ± 1% 2.942n ± 1% ~ (p=0.327 n=20+30)
TypeAssert/TypeAssert[func()_string](func()_string)-8 111.5n ± 1%
geomean 4.004n 6.208n +2.62%
Change-Id: I6a6a6964d6f9c794adc15dc5ff3dc8634b30df89
Reviewed-on: https://go-review.googlesource.com/c/go/+/705255
Reviewed-by: Keith Randall <khr@google.com>
Auto-Submit: Mateusz Poliwczak <mpoliwczak34@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
a5866ebe40
commit
684e8d3363
3 changed files with 35 additions and 11 deletions
|
|
@ -31,3 +31,11 @@ type NonEmptyInterface struct {
|
|||
ITab *ITab
|
||||
Data unsafe.Pointer
|
||||
}
|
||||
|
||||
// CommonInterface describes the layout of both [EmptyInterface] and [NonEmptyInterface].
|
||||
type CommonInterface struct {
|
||||
// Either an *ITab or a *Type, unexported to avoid accidental use.
|
||||
_ unsafe.Pointer
|
||||
|
||||
Data unsafe.Pointer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8783,6 +8783,9 @@ func TestTypeAssertAllocs(t *testing.T) {
|
|||
|
||||
typeAssertAllocs[time.Time](t, ValueOf(new(time.Time)).Elem(), 0)
|
||||
typeAssertAllocs[time.Time](t, ValueOf(*new(time.Time)), 0)
|
||||
|
||||
type I interface{ foo() }
|
||||
typeAssertAllocs[I](t, ValueOf(new(string)).Elem(), 0) // assert fail doesn't alloc
|
||||
}
|
||||
|
||||
func typeAssertAllocs[T any](t *testing.T, val Value, wantAllocs int) {
|
||||
|
|
|
|||
|
|
@ -120,10 +120,16 @@ func (v Value) pointer() unsafe.Pointer {
|
|||
|
||||
// packEface converts v to the empty interface.
|
||||
func packEface(v Value) any {
|
||||
return *(*any)(unsafe.Pointer(&abi.EmptyInterface{
|
||||
Type: v.typ(),
|
||||
Data: packEfaceData(v),
|
||||
}))
|
||||
}
|
||||
|
||||
// packEfaceData is a helper that packs the Data part of an interface,
|
||||
// if v were to be stored in an interface.
|
||||
func packEfaceData(v Value) unsafe.Pointer {
|
||||
t := v.typ()
|
||||
// Declare e as a struct (and not pointer to struct) to help escape analysis.
|
||||
e := abi.EmptyInterface{}
|
||||
// First, fill in the data portion of the interface.
|
||||
switch {
|
||||
case !t.IsDirectIface():
|
||||
if v.flag&flagIndir == 0 {
|
||||
|
|
@ -136,18 +142,15 @@ func packEface(v Value) any {
|
|||
typedmemmove(t, c, ptr)
|
||||
ptr = c
|
||||
}
|
||||
e.Data = ptr
|
||||
return ptr
|
||||
case v.flag&flagIndir != 0:
|
||||
// Value is indirect, but interface is direct. We need
|
||||
// to load the data at v.ptr into the interface data word.
|
||||
e.Data = *(*unsafe.Pointer)(v.ptr)
|
||||
return *(*unsafe.Pointer)(v.ptr)
|
||||
default:
|
||||
// Value is direct, and so is the interface.
|
||||
e.Data = v.ptr
|
||||
return v.ptr
|
||||
}
|
||||
// Now, fill in the type portion.
|
||||
e.Type = t
|
||||
return *(*any)(unsafe.Pointer(&e))
|
||||
}
|
||||
|
||||
// unpackEface converts the empty interface i to a Value.
|
||||
|
|
@ -1544,8 +1547,18 @@ func TypeAssert[T any](v Value) (T, bool) {
|
|||
// TypeAssert[any](ValueOf(1)) == ValueOf(1).Interface().(any)
|
||||
// TypeAssert[error](ValueOf(&someError{})) == ValueOf(&someError{}).Interface().(error)
|
||||
if typ.Kind() == abi.Interface {
|
||||
v, ok := packEface(v).(T)
|
||||
return v, ok
|
||||
// To avoid allocating memory, in case the type assertion fails,
|
||||
// first do the type assertion with a nil Data pointer.
|
||||
iface := *(*any)(unsafe.Pointer(&abi.EmptyInterface{Type: v.typ(), Data: nil}))
|
||||
if out, ok := iface.(T); ok {
|
||||
// Now populate the Data field properly, we update the Data ptr
|
||||
// directly to avoid an additional type asertion. We can re-use the
|
||||
// itab we already got from the runtime (through the previous type assertion).
|
||||
(*abi.CommonInterface)(unsafe.Pointer(&out)).Data = packEfaceData(v)
|
||||
return out, true
|
||||
}
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
|
||||
// Both v and T must be concrete types.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue