此循环在英特尔Conroe/Merom上每3个周期运行一次,imul按预期方式在吞吐量方面存在瓶颈.但是在Haswell/Skylake上,它每11个循环运行一次,显然是因为setnz al它依赖于最后一个循环imul.
; synthetic micro-benchmark to test partial-register renaming
mov ecx, 1000000000
.loop: ; do{
imul eax, eax ; a dep chain with high latency but also high throughput
imul eax, eax
imul eax, eax
dec ecx ; set ZF, independent of old ZF. (Use sub ecx,1 on Silvermont/KNL or P4)
setnz al ; ****** Does this depend on RAX as well as ZF?
movzx eax, al
jnz .loop ; }while(ecx);
Run Code Online (Sandbox Code Playgroud)
如果setnz al …
我一直看到人们声称MOV指令可以在x86中免费,因为寄存器重命名.
对于我的生活,我无法在一个测试用例中验证这一点.每个测试用例我尝试揭穿它.
例如,这是我用Visual C++编译的代码:
#include <limits.h>
#include <stdio.h>
#include <time.h>
int main(void)
{
unsigned int k, l, j;
clock_t tstart = clock();
for (k = 0, j = 0, l = 0; j < UINT_MAX; ++j)
{
++k;
k = j; // <-- comment out this line to remove the MOV instruction
l += j;
}
fprintf(stderr, "%d ms\n", (int)((clock() - tstart) * 1000 / CLOCKS_PER_SEC));
fflush(stderr);
return (int)(k + j + l);
}
Run Code Online (Sandbox Code Playgroud)
这为循环生成以下汇编代码(随意生成这个你想要的;你显然不需要Visual C++):
LOOP:
add edi,esi
mov …Run Code Online (Sandbox Code Playgroud) write(1,"hi",3)在linux上反汇编,gcc -s -nostdlib -nostartfiles -O3结果如下:
ba03000000 mov edx, 3 ; thanks for the correction jester!
bf01000000 mov edi, 1
31c0 xor eax, eax
e9d8ffffff jmp loc.imp.write
Run Code Online (Sandbox Code Playgroud)
我不是到编译器的开发,但由于移动到这些寄存器的每一个值是恒定的和已知的编译时间,我很好奇,为什么不GCC使用dl,dil和al来代替.也许有人会说,此功能不会让任何性能上的差异,但有一个在之间的可执行文件的大小有很大的区别mov $1, %rax => b801000000,并mov $1, %al => b001当我们谈论数千寄存器的程序访问.如果软件的优雅部分不仅体积小,它确实会对性能产生影响.
有人可以解释为什么"海湾合作委员会决定"它无所谓?