Gcc 为什么'mov%eax,%eax;不比不快?
,现代处理器可以判断您是否做了一些愚蠢的事情,例如将寄存器移到自身(Gcc 为什么'mov%eax,%eax;不比不快?,gcc,assembly,x86,cpu-architecture,Gcc,Assembly,X86,Cpu Architecture,,现代处理器可以判断您是否做了一些愚蠢的事情,例如将寄存器移到自身(mov%eax,%eax)并对其进行优化。为了验证该声明,我运行了以下程序: #include <stdio.h> #include <time.h> static inline void f1() { for (int i = 0; i < 100000000; i++) __asm__( "mov %eax, %eax;" "n
mov%eax,%eax
)并对其进行优化。为了验证该声明,我运行了以下程序:
#include <stdio.h>
#include <time.h>
static inline void f1() {
for (int i = 0; i < 100000000; i++)
__asm__(
"mov %eax, %eax;"
"nop;"
);
}
static inline void f2() {
for (int i = 0; i < 100000000; i++)
__asm__(
"nop;"
);
}
static inline void f3() {
for (int i = 0; i < 100000000; i++)
__asm__(
"mov %ebx, %eax;"
"nop;"
);
}
int main() {
int NRUNS = 10;
clock_t t, t1, t2, t3;
t1 = t2 = t3 = 0;
for (int run = 0; run < NRUNS; run++) {
t = clock(); f1(); t1 += clock()-t;
t = clock(); f2(); t2 += clock()-t;
t = clock(); f3(); t3 += clock()-t;
}
printf("f1() took %f cycles on avg\n", (float) t1/ (float) NRUNS);
printf("f2() took %f cycles on avg\n", (float) t2/ (float) NRUNS);
printf("f3() took %f cycles on avg\n", (float) t3/ (float) NRUNS);
return 0;
}
正如人们所料,f3()
的速度最慢。但令人惊讶的是(至少对我来说),f1()
比f2()
快。为什么呢
更新:使用-falign循环编译
在质量上给出了相同的结果:
f1() took 164271.000000 cycles on avg
f2() took 173783.296875 cycles on avg
f3() took 177765.203125 cycles on avg
链接文章中让我认为可以优化的部分是:“move函数负责检查等效位置”
这是指SBCL中的(move r x)
函数,而不是x86mov
指令。它是在从低级中间语言生成代码的过程中进行优化,而不是在运行时通过硬件进行优化
mov%eax,%eax
和nop
都不是完全免费的。它们都需要前端吞吐量,而且在64位模式下,mov%eax,%eax甚至不是NOP(它将eax零扩展到RAX,并且因为是同一个寄存器,mov消除在Intel CPU上失败。)
有关前端/后端吞吐量瓶颈与延迟的更多信息,请参阅
您可能看到了代码对齐的一些副作用,或者像中那样的时髦的Sandybridge系列存储转发延迟效应,因为您也在禁用优化的情况下编译,使编译器生成反优化代码以进行一致的调试,从而将循环计数器保留在内存中。(~6个循环循环通过存储/重新加载来承载依赖链,而不是普通小循环的每个时钟1次迭代。)
如果你的结果在更大的迭代次数下是可复制的,那么你所看到的可能有一些微体系结构的解释,但它可能与你试图测量的任何东西都没有关系
当然,您还需要修复mov%ebx,%eax在启用优化的情况下,成功编译f3
中的code>bug。在不告诉编译器的情况下重击EAX将步进编译器生成的代码。你没有解释你想用它测试什么,所以如果它是一个打字错误,我会打电话给你。这肯定很奇怪。你在哪个处理器上测试过这个?您使用了哪个编译器和哪些标志?进一步注意,clock()
不计算周期,而是每秒时钟数
,因此打印%f周期
会产生误导。另外,当您可以使用双精度时,请不要强制转换为浮点值
。请在打开优化的情况下重试,因为如果没有优化,可能会发生奇怪的事情。还要尝试更改代码,使每个示例至少运行一秒钟,以避免预热或类似问题。您是为32位模式编译还是为64位模式编译?您测量的测试主要是循环开销。对于如此微小的循环体,其他因素可能占主导地位,例如跳转目标对齐。这不是一个解释,但我注意到在您的代码示例中,您并没有像您所说的那样执行mov eax,eax
,而是在我的计算机上执行mov ebx,eax
@BlenderBender(英特尔(R)至强(R)W-2133 CPU@3.60GHz)我得到了142678.703125/142067.000000/143752.093750,没有进行优化,但使用了-falign循环。通过优化,代码不会因为覆盖eax
而终止。
f1() took 164271.000000 cycles on avg
f2() took 173783.296875 cycles on avg
f3() took 177765.203125 cycles on avg