mirror of
https://github.com/golang/go
synced 2025-06-12 17:11:51 +00:00
This is a small-ish adjustment to the change earlier in our stack in CL 649555, which started creating read-only global storage for a composite literal used in an interface conversion and setting the interface data pointer to point to that global storage. In some cases, there are execution-time performance benefits to point to runtime.zeroVal in particular. In reflect, pointer checks against the runtime.zeroVal memory address are used to side-step some work, such as in reflect.Value.Set and reflect.Value.IsZero. In this CL, we therefore dig up the zeroVal symbol, and we use the machinery from earlier in our stack to use a pointer to zeroVal for the interface data pointer if we see examples like: sink = S{} or: s := S{} sink = s CL 649076 (also earlier in our stack) added most of the tests along with debug diagnostics in convert.go to make it easier to test this change. We add a benchmark in reflect to show examples of performance benefit. The left column is our immediately prior CL 649555, and the right is this CL. (The arrays of structs here do not seem to benefit, which we attempt to address in our next CL). goos: linux goarch: amd64 pkg: reflect cpu: Intel(R) Xeon(R) CPU @ 2.80GHz │ cl-649555 │ new │ │ sec/op │ sec/op vs base │ Zero/IsZero/ByteArray/size=16-4 4.176n ± 0% 4.171n ± 0% ~ (p=0.151 n=20) Zero/IsZero/ByteArray/size=64-4 6.921n ± 0% 3.864n ± 0% -44.16% (p=0.000 n=20) Zero/IsZero/ByteArray/size=1024-4 21.210n ± 0% 3.878n ± 0% -81.72% (p=0.000 n=20) Zero/IsZero/BigStruct/size=1024-4 25.505n ± 0% 5.061n ± 0% -80.15% (p=0.000 n=20) Zero/IsZero/SmallStruct/size=16-4 4.188n ± 0% 4.191n ± 0% ~ (p=0.106 n=20) Zero/IsZero/SmallStructArray/size=64-4 8.639n ± 0% 8.636n ± 0% ~ (p=0.973 n=20) Zero/IsZero/SmallStructArray/size=1024-4 79.99n ± 0% 80.06n ± 0% ~ (p=0.213 n=20) Zero/IsZero/Time/size=24-4 7.232n ± 0% 3.865n ± 0% -46.56% (p=0.000 n=20) Zero/SetZero/ByteArray/size=16-4 13.47n ± 0% 13.09n ± 0% -2.78% (p=0.000 n=20) Zero/SetZero/ByteArray/size=64-4 14.14n ± 0% 13.70n ± 0% -3.15% (p=0.000 n=20) Zero/SetZero/ByteArray/size=1024-4 24.22n ± 0% 20.18n ± 0% -16.68% (p=0.000 n=20) Zero/SetZero/BigStruct/size=1024-4 24.24n ± 0% 20.18n ± 0% -16.73% (p=0.000 n=20) Zero/SetZero/SmallStruct/size=16-4 13.45n ± 0% 13.10n ± 0% -2.60% (p=0.000 n=20) Zero/SetZero/SmallStructArray/size=64-4 14.12n ± 0% 13.69n ± 0% -3.05% (p=0.000 n=20) Zero/SetZero/SmallStructArray/size=1024-4 24.62n ± 0% 21.61n ± 0% -12.26% (p=0.000 n=20) Zero/SetZero/Time/size=24-4 13.59n ± 0% 13.40n ± 0% -1.40% (p=0.000 n=20) geomean 14.06n 10.19n -27.54% Finally, here are results from the benchmark example from #71323. Note however that almost all the benefit shown here is from our earlier CL 649555, which is a more general purpose change and eliminates the allocation using a different read-only global than this CL. │ go1.24 │ new │ │ sec/op │ sec/op vs base │ InterfaceAny 112.6000n ± 5% 0.8078n ± 3% -99.28% (p=0.000 n=20) ReflectValue 11.63n ± 2% 11.59n ± 0% ~ (p=0.330 n=20) │ go1.24.out │ new.out │ │ B/op │ B/op vs base │ InterfaceAny 224.0 ± 0% 0.0 ± 0% -100.00% (p=0.000 n=20) ReflectValue 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹ │ go1.24.out │ new.out │ │ allocs/op │ allocs/op vs base │ InterfaceAny 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=20) ReflectValue 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=20) ¹ Updates #71359 Updates #71323 Change-Id: I64d8cf1a7900f011d2ec59b948388aeda1150676 Reviewed-on: https://go-review.googlesource.com/c/go/+/649078 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: David Chase <drchase@google.com>
298 lines
4.9 KiB
Go
298 lines
4.9 KiB
Go
// errorcheck -0 -d=escapedebug=1
|
|
|
|
// Copyright 2024 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Test the data word used for interface conversions
|
|
// that might otherwise allocate.
|
|
|
|
package dataword
|
|
|
|
var sink interface{}
|
|
|
|
func string1() {
|
|
sink = "abc" // ERROR "using global for interface value"
|
|
}
|
|
|
|
func string2() {
|
|
v := "abc"
|
|
sink = v // ERROR "using global for interface value"
|
|
}
|
|
|
|
func string3() {
|
|
sink = "" // ERROR "using global for interface value"
|
|
}
|
|
|
|
func string4() {
|
|
v := ""
|
|
sink = v // ERROR "using global for interface value"
|
|
}
|
|
|
|
func string5() {
|
|
var a any = "abc" // ERROR "using global for interface value"
|
|
_ = a
|
|
}
|
|
|
|
func string6() {
|
|
var a any
|
|
v := "abc"
|
|
a = v // ERROR "using global for interface value"
|
|
_ = a
|
|
}
|
|
|
|
// string7 can be inlined.
|
|
func string7(v string) {
|
|
sink = v
|
|
}
|
|
|
|
func string8() {
|
|
v0 := "abc"
|
|
v := v0
|
|
string7(v) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func string9() {
|
|
v0 := "abc"
|
|
v := v0
|
|
f := func() {
|
|
string7(v)
|
|
}
|
|
f() // ERROR "using global for interface value"
|
|
}
|
|
|
|
func string10() {
|
|
v0 := "abc"
|
|
v := v0
|
|
f := func() {
|
|
f2 := func() {
|
|
string7(v)
|
|
}
|
|
f2()
|
|
}
|
|
f() // ERROR "using global for interface value"
|
|
}
|
|
|
|
func string11() {
|
|
v0 := "abc"
|
|
v := v0
|
|
defer func() {
|
|
string7(v) // ERROR "using global for interface value"
|
|
}()
|
|
}
|
|
|
|
func integer1() {
|
|
sink = 42 // ERROR "using global for interface value"
|
|
}
|
|
|
|
func integer2() {
|
|
v := 42
|
|
sink = v // ERROR "using global for interface value"
|
|
}
|
|
|
|
func integer3() {
|
|
sink = 0 // ERROR "using global for interface value"
|
|
}
|
|
|
|
func integer4a() {
|
|
v := 0
|
|
sink = v // ERROR "using global for interface value"
|
|
}
|
|
|
|
func integer4b() {
|
|
v := uint8(0)
|
|
sink = v // ERROR "using global for single-byte interface value"
|
|
}
|
|
|
|
func integer5() {
|
|
var a any = 42 // ERROR "using global for interface value"
|
|
_ = a
|
|
}
|
|
|
|
func integer6() {
|
|
var a any
|
|
v := 42
|
|
a = v // ERROR "using global for interface value"
|
|
_ = a
|
|
}
|
|
|
|
func integer7(v int) {
|
|
sink = v
|
|
}
|
|
|
|
type M interface{ M() }
|
|
|
|
type MyInt int
|
|
|
|
func (m MyInt) M() {}
|
|
|
|
func escapes(m M) {
|
|
sink = m
|
|
}
|
|
|
|
func named1a() {
|
|
sink = MyInt(42) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named1b() {
|
|
escapes(MyInt(42)) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named2a() {
|
|
v := MyInt(0)
|
|
sink = v // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named2b() {
|
|
v := MyInt(42)
|
|
escapes(v) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named2c() {
|
|
v := 42
|
|
sink = MyInt(v) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named2d() {
|
|
v := 42
|
|
escapes(MyInt(v)) // ERROR "using global for interface value"
|
|
}
|
|
func named3a() {
|
|
sink = MyInt(42) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named3b() {
|
|
escapes(MyInt(0)) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named4a() {
|
|
v := MyInt(0)
|
|
sink = v // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named4b() {
|
|
v := MyInt(0)
|
|
escapes(v) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named4c() {
|
|
v := 0
|
|
sink = MyInt(v) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named4d() {
|
|
v := 0
|
|
escapes(MyInt(v)) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func named5() {
|
|
var a any = MyInt(42) // ERROR "using global for interface value"
|
|
_ = a
|
|
}
|
|
|
|
func named6() {
|
|
var a any
|
|
v := MyInt(42)
|
|
a = v // ERROR "using global for interface value"
|
|
_ = a
|
|
}
|
|
|
|
func named7a(v MyInt) {
|
|
sink = v
|
|
}
|
|
|
|
func named7b(v MyInt) {
|
|
escapes(v)
|
|
}
|
|
|
|
type S struct{ a, b int64 }
|
|
|
|
func struct1() {
|
|
sink = S{1, 1} // ERROR "using global for interface value"
|
|
}
|
|
|
|
func struct2() {
|
|
v := S{1, 1}
|
|
sink = v // ERROR "using global for interface value"
|
|
}
|
|
|
|
func struct3() {
|
|
sink = S{} // ERROR "using global for zero value interface value"
|
|
}
|
|
|
|
func struct4() {
|
|
v := S{}
|
|
sink = v // ERROR "using global for zero value interface value"
|
|
}
|
|
|
|
func struct5() {
|
|
var a any = S{1, 1} // ERROR "using global for interface value"
|
|
_ = a
|
|
}
|
|
|
|
func struct6() {
|
|
var a any
|
|
v := S{1, 1}
|
|
a = v // ERROR "using global for interface value"
|
|
_ = a
|
|
}
|
|
|
|
func struct7(v S) {
|
|
sink = v
|
|
}
|
|
|
|
func emptyStruct1() {
|
|
sink = struct{}{} // ERROR "using global for zero-sized interface value"
|
|
}
|
|
|
|
func emptyStruct2() {
|
|
v := struct{}{}
|
|
sink = v // ERROR "using global for zero-sized interface value"
|
|
}
|
|
|
|
func emptyStruct3(v struct{}) { // ERROR "using global for zero-sized interface value"
|
|
sink = v
|
|
}
|
|
|
|
// Some light emulation of conditional debug printing (such as in #53465).
|
|
|
|
func Printf(format string, args ...any) {
|
|
for _, arg := range args {
|
|
sink = arg
|
|
}
|
|
}
|
|
|
|
var enabled = true
|
|
|
|
func debugf(format string, args ...interface{}) {
|
|
if enabled {
|
|
Printf(format, args...)
|
|
}
|
|
}
|
|
|
|
//go:noinline
|
|
func debugf2(format string, args ...interface{}) {
|
|
if enabled {
|
|
Printf(format, args...)
|
|
}
|
|
}
|
|
|
|
func f1() {
|
|
v := 1000
|
|
debugf("hello %d", v) // ERROR "using global for interface value"
|
|
}
|
|
|
|
func f2() {
|
|
v := 1000
|
|
debugf2("hello %d", v) // ERROR "using global for interface value"
|
|
}
|
|
|
|
//go:noinline
|
|
func f3(i int) {
|
|
debugf("hello %d", i)
|
|
}
|
|
|
|
func f4() {
|
|
f3(1000)
|
|
}
|