Golang中Plan9性能为何不如Go代码的困惑
Golang中Plan9性能为何不如Go代码的困惑 Go代码片段:
func tryAdd(seconds, servid int64) int64
func tryAdd2(seconds, servid int64) int64 {
return seconds + servid
}
Plan9代码片段:
TEXT ·tryAdd(SB), NOSPLIT, $0-24
MOVQ x+0(FP), BX
MOVQ y+8(FP), BP
ADDQ BP, BX
MOVQ BX, ret+16(FP)
RET
压力测试结果显示,tryAdd2(go版本)比tryAdd(plan9版本)快近4倍 BenchmarkTryAdd-4 2000000000 1.34 ns/op BenchmarkTryAdd2-4 2000000000 0.24 ns/op
更多关于Golang中Plan9性能为何不如Go代码的困惑的实战教程也可以访问 https://www.itying.com/category-94-b0.html
非常感谢您的回复!
确实是内联的原因,在关闭内联后它们就会变得相同。
更多关于Golang中Plan9性能为何不如Go代码的困惑的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
也许在Go版本中加法运算被优化掉了。我找到了这个链接 https://rakyll.org/go-tool-flags/ 尝试在没有优化的情况下运行代码?
$ go build -gcflags
用于向Go编译器传递标志。go tool compile -help 列出所有可以传递给编译器的标志。
例如,要禁用编译器优化和内联,可以使用以下gcflags参数。
$ go build -gcflags="-N -l"
从性能测试结果来看,Plan9汇编版本的tryAdd确实比Go版本的tryAdd2慢很多。这主要有几个原因:
1. 函数调用开销
Plan9汇编版本需要处理完整的函数调用约定,包括参数传递和返回值设置,而Go编译器对内联的Go函数有更好的优化。
// Go编译器可能会将tryAdd2完全内联
func BenchmarkTryAdd2(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = tryAdd2(100, 200) // 可能被内联优化
}
}
2. 寄存器分配优化
现代Go编译器在寄存器分配方面比手写汇编更智能:
// Go编译器生成的汇编可能更高效
// 可能会直接在寄存器中操作,避免内存访问
3. 汇编代码优化空间
你的Plan9汇编代码有优化空间:
TEXT ·tryAddOptimized(SB), NOSPLIT, $0-24
MOVQ x+0(FP), AX // 使用AX寄存器,更通用
ADDQ y+8(FP), AX // 直接加到AX
MOVQ AX, ret+16(FP) // 设置返回值
RET
4. 编译器内联优势
Go版本的关键优势在于内联:
// 在实际使用中,编译器可能会这样优化:
result := a + b // 直接替换函数调用
性能对比示例
// 测试显示内联的巨大优势
func benchmarkComparison() {
// tryAdd2的调用可能被完全优化掉
// 而tryAdd的汇编调用必须保留完整的函数调用框架
}
在现代Go版本中,除非有非常特定的优化需求,否则纯Go代码通常比手写汇编性能更好,因为编译器能够进行全局优化和内联处理。只有在极少数需要特定CPU指令或微优化的情况下,手写汇编才有意义。

