1 回答

TA贡献2037条经验 获得超6个赞
首先,正如@kostix所说,只有一个堆栈增长,因此基准测试不是这些测量的正确工具。我试图测量和函数的单次调用,但仍然得到了意想不到的结果:115ns vs 15us。stack()heap()
所以我从顶部开始,找出哪个编译器确实在优化我的代码并完全删除堆栈分配。有 调用 ,但没有堆栈分配。stackit()
我重写了示例并添加了一个打印件,以找到堆栈增长的时刻。
package main
import (
"fmt"
"time"
)
const size = 256
func main() {
print("main\n")
start := time.Now()
stack()
fmt.Println(time.Since(start))
}
//go:noinline
func stack() {
print("stack\n")
x := [size]int64{}
end(x)
}
//go:noinline
func heap() {
print("heap\n")
_ = heapit()
}
//go:noinline
func end(x [size]int64) {
_ = x
}
//go:noinline
func heapit() *[size]int64 {
return &[size]int64{}
}
使用编译的 go 版本运行此版本,得到以下输出:stackDebug = 1
<...>
main
runtime: newstack sp=0xc000070f10 stack=[0xc000070000, 0xc000071000]
morebuf={pc:0x10a2dcc sp:0xc000070f20 lr:0x0}
sched={pc:0x10a2f09 sp:0xc000070f18 lr:0x0 ctxt:0x0}
stackalloc 16384
stackcacherefill order=3
allocated 0xc00010a000
copystack gp=0xc000000180 [0xc000070000 0xc000070f18 0xc000071000] -> [0xc00010a000 0xc00010df18 0xc00010e000]/16384
stackfree 0xc000070000 4096
stack grow done
stack
50.096µs
现在,堆栈显然正在增长到 16384 字节。这里重要的部分是将旧的堆栈复制到新的内存区域。
堆版本不会更改堆栈的大小:
main
heap
6.096µs
这个数字似乎不公平,所以我仔细检查了堆分配的内容,然后再次运行基准测试:
逃生分析结果:
> go build -gcflags '-m -l' -o sstack ./main.go
./main.go:37:9: &[256]int64{} escapes to heap
<...>
基准测试结果:
> go test -bench=. -benchmem
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkStackIt-12 24075922 43.80 ns/op 0 B/op 0 allocs/op
BenchmarkHeapIt-12 5042449 236.8 ns/op 2048 B/op 1 allocs/op
PASS
ok _/Users/asnelzin/dev/experiment/sstack 2.804s
因此,是的,创建新堆栈并将旧堆栈的内容复制到新创建的堆栈会产生开销。但从长远来看,这种开销将与堆分配的开销相形见绌。
- 1 回答
- 0 关注
- 141 浏览
添加回答
举报