以下是本书中的代码片段" The Go Programming Language":
for t := 0.0; t < cycles*2*math.Pi; t += res {
...
}
似乎必须在for循环的每次迭代之前计算for循环条件t < cycles*2*math.Pi
中的表达式。或者,编译器是否通过预先计算表达式的结果来优化这一点(假设在迭代期间没有变量变化)?上述编码风格是否会影响性能?
答案 0 :(得分:5)
这实际上取决于Go版本,但go version go1.7 windows/amd64
似乎确实计算了一次值。
转到代码:
var cycles = 10.0
var res = 1000.0
for t := 0.0; t < cycles*2*math.Pi; t += res {
}
Asm代码:
movsd [rsp+58h+var_20], xmm0
mov [rsp+58h+var_18], 0
mov [rsp+58h+var_10], 0
lea rax, qword_494500
mov [rsp+58h+var_58], rax
lea rax, [rsp+58h+var_20]
mov [rsp+58h+var_50], rax
mov [rsp+58h+var_48], 0
call runtime_convT2E
mov rax, [rsp+58h+var_40]
mov rcx, [rsp+58h+a] ; a
mov [rsp+58h+var_18], rax
mov [rsp+58h+var_10], rcx
lea rax, [rsp+58h+var_18]
mov [rsp+58h+var_58], rax
mov [rsp+58h+var_50], 1
mov [rsp+58h+var_48], 1
call fmt_Println
movsd xmm0, cs:$f64_408f400000000000
movsd xmm1, [rsp+58h+t]
addsd xmm0, xmm1
movsd [rsp+58h+t], xmm0
movsd xmm1, cs:$f64_404f6a7a2955385e
ucomisd xmm1, xmm0
ja loc_401083
f64_404f6a7a2955385e
是预先计算的双倍值,等于10 * 2 * math.Pi
或62.8318530718
将编译器recently switched转到SSA,因此这些优化会随着它们从中受益而不断改进。目前SSA仅适用于amd64:
编译工具链
此版本包含64位x86的新代码生成后端 系统遵循2015年正在制定的提案 自那时候起。基于SSA的新后端产生更紧凑,更多 高效的代码,并提供更好的优化平台,如 边界检查消除。
1.8 should have it for all supported architectures:
编译工具链
Go 1.7为64位x86系统引入了新的编译器后端。在Go 1.8,后端已经进一步发展,现在用于所有 架构。
答案 1 :(得分:2)
它似乎没有优化,并且没有列在&#34; Compiler And Runtime Optimizations&#34;。
gc编译器不进行任何循环优化。该编译器的主要目标之一是快速编译。虽然改进的优化总是有用的,但它必须符合该目标
但这可能已经改变了。在&#34; Recursion And Tail Calls In Go&#34;中可以看到一个很好的技术来查看正在发生的事情,您可以在其中查看由go程序生成的汇编代码。
另请参阅最近的2016&#34; Reversing GO binaries like a pro&#34;文章。
并且&#34; go.godbolt.org&#34;也可以提供帮助:请参阅assembly code here。
你可以看到&#34; t < cycles*2*math.Pi
&#34;部分始终评估
.L2:
movsd xmm0, QWORD PTR [rbp-24]
addsd xmm0, xmm0
movsd xmm1, QWORD PTR .LC2[rip]
mulsd xmm0, xmm1
ucomisd xmm0, QWORD PTR [rbp-8]
seta al
test al, al
答案 2 :(得分:2)
当前的Go编译器不会在循环外移动循环不变计算。
编译器的传递列表可以在https://github.com/golang/go/blob/master/src/cmd/compile/internal/ssa/compile.go#L329
中看到在@creker的示例中,编译器执行常量折叠,而不是循环不变代码运动。
作为旁注,我几个月前确实为Go编译器制作了LICM传递https://github.com/golang/go/compare/master...momchil-velikov:dev.chill.licm
在通常使用的Go基准测试中并没有很好地提高性能。 (我责怪残暴的登记册分配:P)