Go运行时是否每次迭代都会评估for循环条件?

时间:2016-12-26 07:39:44

标签: performance go compiler-optimization

以下是本书中的代码片段" The Go Programming Language":

for t := 0.0; t < cycles*2*math.Pi; t += res {
    ...
}

似乎必须在for循环的每次迭代之前计算for循环条件t < cycles*2*math.Pi中的表达式。或者,编译器是否通过预先计算表达式的结果来优化这一点(假设在迭代期间没有变量变化)?上述编码风格是否会影响性能?

3 个答案:

答案 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.Pi62.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;。

this older discussion中所述,

  

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)