为什么`mov%eax,%eax; nop比nop快?

时间:2018-10-25 15:14:11

标签: gcc assembly x86 cpu-architecture

Apparently,现代处理器可以判断您是否做了一些愚蠢的事情,例如将寄存器移到其自身(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;
}

这给了我

f1() took 175587.093750 cycles on avg
f2() took 188313.906250 cycles on avg
f3() took 194654.296875 cycles on avg

正如人们所期望的,f3()的发布速度最慢。但是令人惊讶的是(至少对我来说),f1()f2()快。为什么会这样?

更新:使用-falign-loops进行编译可得出定性的结果:

f1() took 164271.000000 cycles on avg
f2() took 173783.296875 cycles on avg
f3() took 177765.203125 cycles on avg

1 个答案:

答案 0 :(得分:2)

  

链接文章中让我认为可以对其进行优化的部分是:“移动功能负责检查等效位置”

那是在谈论SBCL中的(move r x) 功能,而不是x86 mov 指令。讨论的是从低级中间语言生成代码期间的优化,而不是硬件在运行时的优化。

mov %eax, %eaxnop都不是完全免费的。它们都消耗前端吞吐量,并且mov %eax,%eax甚至在64位模式下也不是NOP(它将EAX零扩展为RAX,因为它是相同的寄存器,因此在Intel CPU上无法进行移动消除。)< / p>

有关前端/后端吞吐量瓶颈与延迟的关系,请参见Can x86's MOV really be "free"? Why can't I reproduce this at all?


您可能会看到代码对齐的一些副作用,或者像Adding a redundant assignment speeds up code when compiled without optimization中那样出现了时髦的Sandybridge系列存储转发延迟效应,因为您还禁用了优化功能进行编译,从而使编译器反-用于一致调试的优化代码,可将循环计数器保留在内存中。 (通过一个存储/重载,大约有6个循环的循环依赖链,而不是正常的微小循环每个时钟1次迭代。)

如果您的结果可以通过较大的迭代次数进行重现,则可能对您所看到的内容有一些微体系结构的解释,但可能与您尝试测量的内容无关。

当然,您还需要修复mov %ebx, %eax;中的f3错误,以在启用优化的情况下成功编译。在不通知编译器的情况下破坏EAX将会依靠编译器生成的代码。您没有解释要使用的测试内容,因此请IDK(如果是错字的话)。