首页 > 解决方案 > 为什么`mov %eax, %eax; nop` 比`nop` 快?

问题描述

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

标签: gccassemblyx86cpu-architecture

解决方案


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

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

两者都不mov %eax, %eaxnop完全免费的。它们都消耗前端吞吐量,mov %eax,%eax甚至不是 64 位模式下的 NOP(它将 EAX 零扩展为 RAX,并且因为它是相同的寄存器移动消除在 Intel CPU 上失败。)

看看x86 的 MOV 真的可以“免费”吗?为什么我根本无法重现这个?有关前端/后端吞吐量瓶颈与延迟的更多信息。


您可能会看到代码对齐的一些副作用,或者可能是一种时髦的 Sandybridge 家族存储转发延迟效应,例如添加冗余分配在没有优化的情况下编译时会加速代码,因为您也在禁用优化的情况下进行编译,让您的编译器制作反优化代码以进行一致的调试,将循环计数器保存在内存中。(通过存储/重新加载约 6 周期循环携带的依赖链,而不是普通微小循环的每时钟 1 次迭代。)

如果您的结果可以通过更大的迭代次数重现,那么您所看到的可能有一些微架构解释,但它可能与您尝试测量的任何内容无关。

当然,您还需要修复mov %ebx, %eax;错误f3才能在启用优化的情况下成功编译。在不告诉编译器的情况下破坏 EAX 将踩到编译器生成的代码。你没有解释你试图用它来测试什么,所以如果它是一个错字,请 IDK。


推荐阅读