Go version
go version go1.27-devel_06bb174f89 Mon Jun 29 10:05:16 2026 -0700 linux/amd64
Output of go env in your module/workspace:
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN='/usr/local/google/home/shaojunyang/go/bin'
GOCACHE='/usr/local/google/home/shaojunyang/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/usr/local/google/home/shaojunyang/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build564480818=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/usr/local/google/home/shaojunyang/work/go/src/go.mod'
GOMODCACHE='/usr/local/google/home/shaojunyang/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPACKAGESDRIVER=''
GOPATH='/usr/local/google/home/shaojunyang/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/google/home/shaojunyang/work/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/usr/local/google/home/shaojunyang/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/google/home/shaojunyang/work/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.27-devel_06bb174f89 Mon Jun 29 10:05:16 2026 -0700'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
I was playing around with my SROA pass to find regressions introduced by its code transformation, and found a noisy behavior of regalloc.
I constructed 2 benchmark functions, both of which consists of 64 variables and they are made circularly dependent inside a loop. See https://go.dev/play/p/iVH206DXP9L?v=gotip. (Playground will timeout on this, please copy them to local).
randomizedInit initializes the variables randomly, while kindoforderedInit initialized them like an arithmetic sequence. All variables are assigned to the same initial value. And the codes followed (the loop that introduced a loop-carried circular dependency) are the same for both benchmark functions.
These 2 functions should be semantically isomorphic, the only difference is that the variables' SSA values are listed in a different order. So I was expecting them to be roughly the same performance, but the result is pretty different:
Running benchmarks...
BenchmarkKindofordered: 5754 19112 ns/op
BenchmarkRandomized: 4636 26018 ns/op
It might be that a stack storming is happening for the randomizedInit benchmark: when merging edges, we have to move values that are in spilled around. When reaglloc tries to allocate a scratch register, it needs to spill more value. And somehow if the order of these chained spills are not aligned nicely, we end up moving values back and forth, i.e. doing a unnecessary stack permutation.
What did you see happen?
Running benchmarks...
BenchmarkKindofordered: 5754 19112 ns/op
BenchmarkRandomized: 4636 26018 ns/op
What did you expect to see?
The benchmarks perform the same.
Go version
go version go1.27-devel_06bb174f89 Mon Jun 29 10:05:16 2026 -0700 linux/amd64
Output of
go envin your module/workspace:What did you do?
I was playing around with my SROA pass to find regressions introduced by its code transformation, and found a noisy behavior of regalloc.
I constructed 2 benchmark functions, both of which consists of 64 variables and they are made circularly dependent inside a loop. See https://go.dev/play/p/iVH206DXP9L?v=gotip. (Playground will timeout on this, please copy them to local).
randomizedInitinitializes the variables randomly, whilekindoforderedInitinitialized them like an arithmetic sequence. All variables are assigned to the same initial value. And the codes followed (the loop that introduced a loop-carried circular dependency) are the same for both benchmark functions.These 2 functions should be semantically isomorphic, the only difference is that the variables' SSA values are listed in a different order. So I was expecting them to be roughly the same performance, but the result is pretty different:
It might be that a stack storming is happening for the
randomizedInitbenchmark: when merging edges, we have to move values that are in spilled around. Whenreaglloctries to allocate a scratch register, it needs to spill more value. And somehow if the order of these chained spills are not aligned nicely, we end up moving values back and forth, i.e. doing a unnecessary stack permutation.What did you see happen?
What did you expect to see?
The benchmarks perform the same.