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)
函数,而不是x86
mov
指令。它是在从低级中间语言生成代码的过程中进行优化,而不是在运行时通过硬件进行优化

mov%eax,%eax
nop
都不是完全免费的。它们都需要前端吞吐量,而且在64位模式下,
mov%eax,%eax甚至不是NOP(它将eax零扩展到RAX,并且因为是同一个寄存器,mov消除在Intel CPU上失败。)

有关前端/后端吞吐量瓶颈与延迟的更多信息,请参阅


您可能看到了代码对齐的一些副作用,或者像中那样的时髦的Sandybridge系列存储转发延迟效应,因为您也在禁用优化的情况下编译,使编译器生成反优化代码以进行一致的调试,从而将循环计数器保留在内存中。(~6个循环循环通过存储/重新加载来承载依赖链,而不是普通小循环的每个时钟1次迭代。)

如果你的结果在更大的迭代次数下是可复制的,那么你所看到的可能有一些微体系结构的解释,但它可能与你试图测量的任何东西都没有关系


当然,您还需要修复
mov%ebx,%eaxf3
中的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