-
Notifications
You must be signed in to change notification settings - Fork 18.5k
Description
Go version
go version go1.22.6 darwin/arm64
Output of go env in your module/workspace:
GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/xxx/Library/Caches/go-build'
GOENV='/Users/xxx/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/xxx/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/xxx/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.22.6/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.22.6/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.22.6'
GCCGO='gccgo'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/6n/4bnk08l915n2qsvtzm_lmtv40000gn/T/go-build2946791027=/tmp/go-build -gno-record-gcc-switches -fno-common'What did you do?
I'm trying to add a basic tracing library to our code and found out so many example when golang doesn't inline basic cases, so providing here a "simplified" to the minimum examples w/o code doing a real tracing.
What did you see happen?
example 1
package main
import "fmt"
var level = 0
func StartSpan(name string) {
if level&1 != 0 {
fmt.Println("StartSpan")
}
}
func main() {
StartSpan("span")
}
StartSpan doesn't inline in this example in main().
gcflags -m says that './main.go:9:15: "StartSpan" escapes to heap', though it is unclear what exactly escapes here and why it is a problem for const string.
Seems this is a problem as in tracing one frequently need to provide variadic number of attributes.
Replacing fmt.Println() to some non-variadic function helps in this example.
Example 2
Ok, lets make compiler to inline StartSpan by replacing Println() to a different function, but having a variadic KV list:
func StartSpan(name string, kv ...KV) {
if level&1 != 0 {
// do smth useless, just need a call to some other func here
fn(name, kv)
}
}
var list []KV
func fn(name string, kv []KV) {
// do smth with kv... e.g. copy elements to some global list
for idx := range kv {
list = append(list, kv[idx])
}
}
func main() {
StartSpan("span", KV{"k1", "v1"}, KV{"k2", "v2"})
}
now StartSpan is inlined to main, but its arguments are prepared BEFORE check for if level&1 != 0 and it takes significant amount of code like this (actually, I saw 2 pages of asm with allocations and WriteBarrier in real life):
main.main STEXT size=432 args=0x0 locals=0xb8 funcid=0x0 align=0x0
...
0x001c 00028 (/tmp/test/main.go:27) STP (ZR, ZR), main..autotmp_6-80(SP)
0x0020 00032 (/tmp/test/main.go:27) STP (ZR, ZR), main..autotmp_6-64(SP)
0x0024 00036 (/tmp/test/main.go:27) STP (ZR, ZR), main..autotmp_6-48(SP)
0x0028 00040 (/tmp/test/main.go:27) STP (ZR, ZR), main..autotmp_6-32(SP)
0x002c 00044 (/tmp/test/main.go:27) MOVD $2, R5
0x0030 00048 (/tmp/test/main.go:27) MOVD R5, main..autotmp_6-72(SP)
0x0034 00052 (/tmp/test/main.go:27) MOVD $go:string."k1"(SB), R6
0x003c 00060 (/tmp/test/main.go:27) MOVD R6, main..autotmp_6-80(SP)
0x0040 00064 (/tmp/test/main.go:27) MOVD R5, main..autotmp_6-56(SP)
0x0044 00068 (/tmp/test/main.go:27) MOVD $go:string."v1"(SB), R6
0x004c 00076 (/tmp/test/main.go:27) MOVD R6, main..autotmp_6-64(SP)
0x0050 00080 (/tmp/test/main.go:27) MOVD R5, main..autotmp_6-40(SP)
0x0054 00084 (/tmp/test/main.go:27) MOVD $go:string."k2"(SB), R6
0x005c 00092 (/tmp/test/main.go:27) MOVD R6, main..autotmp_6-48(SP)
0x0060 00096 (/tmp/test/main.go:27) MOVD R5, main..autotmp_6-24(SP)
0x0064 00100 (/tmp/test/main.go:27) MOVD $go:string."v2"(SB), R5
0x006c 00108 (/tmp/test/main.go:27) MOVD R5, main..autotmp_6-32(SP)
0x0070 00112 (<unknown line number>) NOP
0x0070 00112 (<unknown line number>) PCDATA $0, $-3
while the level flag is checked just here:
0x0070 00112 (/tmp/test/main.go:11) MOVD main.level(SB), R5
0x0078 00120 (/tmp/test/main.go:11) PCDATA $0, $-1
0x0078 00120 (/tmp/test/main.go:11) TBZ $0, R5, 132
...
so it seems like compiler is absolutely incapable to detect and reorder code efficiently in situations like this... :/
I believe such situations are pretty common in loggers, tracing and other typical scenarios and it deserves optimization.
What did you expect to see?
efficient inlining of function StartSpan in both examples and checking level variable BEFORE initializing lots of argument objects.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status