Azs*_*sgy 8 c optimization assembly gcc compilation
我在最近关于array[i++]vs 的假设速度的争论中写了这个片段array[i]; i++.
int array[10];
int main(){
int i=0;
while(i < 10){
array[i] = 0;
i++;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译器资源管理器的代码段:https://godbolt.org/g/de7TY2
正如所预期的,编译器输出相同ASM array[i++]和array[i]; i++与至少-O1.然而令我感到惊讶的是,xor eax, eax在更高的优化级别中,看似随机地放置在函数中.
在-O2,GCC会将xor在之前ret预期
mov DWORD PTR [rax], 0
add rax, 4
cmp rax, OFFSET FLAT:array+40
jne .L2
xor eax, eax
ret
Run Code Online (Sandbox Code Playgroud)
然而,它在第二个之后将xor置于mov-O3
mov QWORD PTR array[rip], 0
mov QWORD PTR array[rip+8], 0
xor eax, eax
mov QWORD PTR array[rip+16], 0
mov QWORD PTR array[rip+24], 0
mov QWORD PTR array[rip+32], 0
ret
Run Code Online (Sandbox Code Playgroud)
icc通常放在-O1:
push rsi
xor esi, esi
push 3
pop rdi
call __intel_new_feature_proc_init
stmxcsr DWORD PTR [rsp]
xor eax, eax
or DWORD PTR [rsp], 32832
ldmxcsr DWORD PTR [rsp]
..B1.2:
mov DWORD PTR [array+rax*4], 0
inc rax
cmp rax, 10
jl ..B1.2
xor eax, eax
pop rcx
ret
Run Code Online (Sandbox Code Playgroud)
但是在一个陌生的地方 -O2
push rbp
mov rbp, rsp
and rsp, -128
sub rsp, 128
xor esi, esi
mov edi, 3
call __intel_new_feature_proc_init
stmxcsr DWORD PTR [rsp]
pxor xmm0, xmm0
xor eax, eax
or DWORD PTR [rsp], 32832
ldmxcsr DWORD PTR [rsp]
movdqu XMMWORD PTR array[rip], xmm0
movdqu XMMWORD PTR 16+array[rip], xmm0
mov DWORD PTR 32+array[rip], eax
mov DWORD PTR 36+array[rip], eax
mov rsp, rbp
pop rbp
ret
Run Code Online (Sandbox Code Playgroud)
和-O3
and rsp, -128
sub rsp, 128
mov edi, 3
call __intel_new_proc_init
stmxcsr DWORD PTR [rsp]
xor eax, eax
or DWORD PTR [rsp], 32832
ldmxcsr DWORD PTR [rsp]
mov rsp, rbp
pop rbp
ret
Run Code Online (Sandbox Code Playgroud)
只有clang xor直接放在ret所有优化级别的前面:
xorps xmm0, xmm0
movaps xmmword ptr [rip + array+16], xmm0
movaps xmmword ptr [rip + array], xmm0
mov qword ptr [rip + array+32], 0
xor eax, eax
ret
Run Code Online (Sandbox Code Playgroud)
由于GCC和ICC都在更高的优化级别上执行此操作,因此我认为必定有某种充分的理由.
为什么有些编译器会这样做?
代码在语义上是相同的,编译器可以按照自己的意愿重新排序,但由于这只会在更高的优化级别进行更改,因此必须由某种优化引起.
由于eax未使用,编译器可以随时将寄存器归零,并且按预期工作.
你没注意到的一个有趣的事情是icc -O2版本:
xor eax, eax
or DWORD PTR [rsp], 32832
ldmxcsr DWORD PTR [rsp]
movdqu XMMWORD PTR array[rip], xmm0
movdqu XMMWORD PTR 16+array[rip], xmm0
mov DWORD PTR 32+array[rip], eax ; set to 0 using the value of eax
mov DWORD PTR 36+array[rip], eax
Run Code Online (Sandbox Code Playgroud)
注意eax返回值为零,但也用于归零2个存储区(最后2条指令),这可能是因为使用的指令eax短于具有立即零操作数的指令.
所以两只一石二鸟.
不同的指令具有不同的延迟.有时,更改指令的顺序可以加速代码,原因有几个.例如:如果某个指令需要几个周期才能完成,如果它在函数末尾,程序就会等待它完成.如果它在函数的早期,则在该指令完成时可能发生其他事情.不过,这里的实际原因不太可能是第二个想法,因为我认为寄存器是低延迟指令.但是,延迟是依赖于处理器的.
但是,放置XOR可能与分离放置它的mov指令有关.
还有一些优化可以利用现代处理器的优化功能,例如流水线,分支预测(在我看来就不是这种情况......)等等.您需要对这些功能有深入的了解才能理解优化器可以做些什么来利用它们.
你可能会发现这个信息丰富.它向我指出了一个我以前从未见过的资源,但有很多你想要的信息(或者不想要:-))知道但是却不敢问:-) 这里
| 归档时间: |
|
| 查看次数: |
307 次 |
| 最近记录: |