代码之家  ›  专栏  ›  技术社区  ›  BlenderBender

为什么'mov%eax,%eax;不比不快?

  •  4
  • BlenderBender  · 技术社区  · 6 年前

    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 回复  |  直到 6 年前
        1
  •  2
  •   Peter Cordes Steve Bohrer    6 年前

    链接文章中让我觉得可以优化的部分是:“move函数负责检查等效位置”

    这是关于 (move r x) 作用 在SBCL中,不是x86 mov 指示 而不是从运行时的硬件优化阶段。

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

    看见 Can x86's MOV really be "free"? Why can't I reproduce this at all? 有关前端/后端吞吐量瓶颈与延迟的更多信息。


    您可能会看到代码对齐的一些副作用,或者像中一样的时髦的Sandybridge家族商店转发延迟效应 Adding a redundant assignment speeds up code when compiled without optimization 因为您也在禁用优化的情况下编译,所以让编译器生成反优化代码以进行一致性调试,从而将循环计数器保留在内存中。(~6个循环循环通过存储/重新加载来承载依赖链,而不是普通小循环的每个时钟1次迭代。)

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

    当然,你还需要解决这个问题 mov %ebx, %eax; 插嘴 f3 在启用优化的情况下成功编译。在不告诉编译器的情况下重击EAX会踩到编译器生成的代码。你没有解释你想用它测试什么,所以如果它是一个打字错误。