看了一篇博客,讲 go 不同写法的性能差异,不太理解底层实现

332 天前
 seekseat

https://mp.weixin.qq.com/s/LIdlk0p32iW2KDjGHGflMQ

这最后怎么得出的 r+0 更快的?什么道理....面试时会被问到吗

2703 次点击
所在节点    Go 编程语言
17 条回复
KaynW
332 天前
谁问你这个给他两个大逼斗
token10086
332 天前
茴字有几种写法?
hollc
332 天前
这种性能差异我觉得其实体现出的是底层实现的 bug ,毫无意义的代码反而效率更高
hxtheone
332 天前
普通开发者了解 go 编译器里寄存器的使用就可以了, 学会文中这点对日常开发没什么太大帮助, 赞同文章的最后一句话, 这些细节差异应该是编译器处理没必要暴露给开发者
wangritian
332 天前
面试问这种问题就不是想招好好干活的
看上去是多了+0 之后,触发了编译器某个优化逻辑,让上面循环过程中使用的变量停留在寄存器,而不用写回内存再读出来,确实是编译器内部问题,开发者不用管
rrfeng
332 天前
开头直接猜编译器优化问题,
看到开始汇编了,以为最后会解释,结果没有??

那这是狗屁文章。。。
rrfeng
332 天前
无责任猜测:

在 g 里传给 chan 的是 (r+0) 是一个「临时」值,r 没有被传递,因此 r 被优化放到了寄存器上。
而在 f 里要传给 chan 的是变量 r ,所以它不能被优化到寄存器里,只能放到栈上。

所以是寄存器优先原则的作用。
rrfeng
332 天前
补充一点:文章引用 2 里介绍了 Go 函数参数都是「栈传递」,所以上述解释应该正确
lxdlam
332 天前
一方面我同意 #3 ,更像是编译器的 bug ,另一方面我本地似乎复现不了这个差距如此巨大的结果,估计可以看下 1.21rc 到现在( go 1.21.6 ) 的差异。

而对于一定程度上的差异( 100000ns per op ),单纯从生成代码上来看,f 生成的函数直接对 r 做了修改,所以需要一次对 r 的 load ,而 g 是对一个临时变量做修改,虽然二者都是一次 load 跟一次 store ,但是 r 毕竟不好说分配在哪儿(也许在 heap 上,也许在 register ,看编译器优化),那么 r 确实可能比起临时变量( go 倾向于分配在 register 上)的读写要更慢。至于为什么会有如此差异,实际上应该是因为编译器识别出来了这个累加 pattern ,而在 f 里因为没有额外操作,所以直接对 r 进行操作,把加数这些都当 immediate value 优化成单次 INCQ 了;而在 g 中,由于又读到了 r + 0 ,编译器首先优化成了将其写入中间变量的操作,又在后续 pass 中发现其实对 r 基本无操作,去掉了这里面所有 r 的主动 reference ,将其完全优化到了完全只读写中间变量,所以生成了这个样子的代码。

以上仅抛砖引玉,我不是 plan9 和 go compiler 专家,只能看个大概,这里面同样可能会有很多说不清的其他因素影响。但是我仍然同意,这种 case 应该 report 给官方去修改,而不是当新时代的语言律师模拟考题,同样,如果在乎这个粒度的性能差距,可能我们会选择更精细的语言和优化方式了,而不是在这继续抄写茴字剩下的写法。
rrfeng
332 天前
@lxdlam 同意,除了我觉得编译器优化逻辑只要不出错就不是 bug 。毕竟不优化的地方多了去了……

对 f 要优化的话需要分析整个函数里变量的引用关系(诶…怎么变成 rust 了??
pkoukk
332 天前
同意楼上,这应该是编译器可以优化的问题,不应该作为一个 feature 存在
谁面试问这种问题,就给他两个大逼斗
bybyte
332 天前
语言层面上这两者完全是等价的吧,底层差异这么大那就是编译器的问题了
mainjzb
332 天前
这样的编译几乎和 g 一样,应该是堆栈导致,go 的编译器确实垃圾。。对比 rust/C/C++

func g2(n int, c chan<- int) {
r := 0
for i := 0; i < n; i++ {
r += 1
}
tmp := r
c <- tmp
}
mainjzb
332 天前
进一步信息,这个问题只出现在 1.21 和 1.20 中,其他版本编译没有问题。
根据 https://go.godbolt.org/ 提供的反编译信息
bv
332 天前
你们上来就争论,却不跑一遍作者的示例代码。
Linux Ubuntu i5-8500 测试结果是:

go1.21.6 下确实如那篇文章所说 g 明显比 f 快。
go1.22rc2 下 g 和 f 效率几乎无差异。
Kumo31
332 天前
go compiler 的编译优化确实一言难尽,之前测试过,很基础的循环展开都不会做,官方美名其曰「保证编译速度」: https://github.com/golang/go/issues/51302
seekseat
323 天前
学到了,蟹蟹大佬们

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1011483

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX