如果我针对大小而不是速度进行优化,为什么GCC会生成15-20%的更快代码?


445

我在2009年首先注意到,如果我对大小-Os)而不是速度(-O2-O3)进行优化,那么GCC(至少在我的项目和我的机器上)倾向于生成明显更快的代码,而我一直在想为什么。

我设法创建了(相当愚蠢的)代码来显示这种令人惊讶的行为,并且足够小,可以在此处发布。

const int LOOP_BOUND = 200000000;

__attribute__((noinline))
static int add(const int& x, const int& y) {
    return x + y;
}

__attribute__((noinline))
static int work(int xval, int yval) {
    int sum(0);
    for (int i=0; i<LOOP_BOUND; ++i) {
        int x(xval+sum);
        int y(yval+sum);
        int z = add(x, y);
        sum += z;
    }
    return sum;
}

int main(int , char* argv[]) {
    int result = work(*argv[1], *argv[2]);
    return result;
}

如果使用编译-Os,则需要0.38 s来执行此程序,如果使用-O2或编译,则需要0.44 s的时间-O3。始终获得这些时间并且几乎没有噪音(gcc 4.7.2,x86_64 GNU / Linux,Intel Core i5-3320M)。

(更新:我已将所有汇编代码移至GitHub:他们使帖子made肿,并且由于fno-align-*标志具有相同的作用,因此显然对问题的价值很小。)

这是使用-Os和生成的程序集-O2

不幸的是,我对程序集的理解非常有限,所以我不知道接下来的操作是否正确:我抓住了程序集,-O2并将-Os .p2align之外的所有差异合并到了程序集中,这里是结果。这段代码仍然可以在0.38s内运行,唯一的不同是 .p2align 东西。

如果我猜对了,这些是用于堆栈对齐的填充。根据为什么GCC垫与NOP一起起作用?这样做是为了希望代码能更快地运行,但是显然,这种优化在我看来是适得其反的。

在这种情况下,罪魁祸首是填充物吗?为什么以及如何?

它产生的噪声使定时微优化变得不可能。

当我对C或C ++源代码进行微优化(与堆栈对齐无关)时,如何确保这种偶然的幸运/不幸的对齐方式不会受到干扰?


更新:

遵循Pascal Cuoq的回答,我对对齐方式进行了一些修改。通过传递-O2 -fno-align-functions -fno-align-loops给gcc,所有内容.p2align都从程序集中删除,生成的可执行文件在0.38s内运行。根据gcc文档

-Os启用所有-O2优化[但是] -Os禁用以下优化标志:

  -falign-functions  -falign-jumps  -falign-loops
  -falign-labels  -freorder-blocks  -freorder-blocks-and-partition
  -fprefetch-loop-arrays

因此,这似乎是一个(错误)对齐问题。

我仍然持怀疑态度,-march=native因为在建议萨芬杜汉的回答。我不相信这不仅会干扰这一(错)对齐问题;它对我的机器绝对没有影响。(不过,我支持他的回答。)


更新2:

我们可以-Os拍照。以下时间是通过编译获得的

  • -O2 -fno-omit-frame-pointer 0.37秒

  • -O2 -fno-align-functions -fno-align-loops 0.37秒

  • -S -O2然后add()work()0.37s 之后手动移动组件

  • -O2 0.44秒

在我看来,离add()呼叫站点的距离很重要。我已经试过perf,但输出perf statperf report让人很没有意义了我。但是,我只能从中得到一个一致的结果:

-O2

 602,312,864 stalled-cycles-frontend   #    0.00% frontend cycles idle
       3,318 cache-misses
 0.432703993 seconds time elapsed
 [...]
 81.23%  a.out  a.out              [.] work(int, int)
 18.50%  a.out  a.out              [.] add(int const&, int const&) [clone .isra.0]
 [...]
       ¦   __attribute__((noinline))
       ¦   static int add(const int& x, const int& y) {
       ¦       return x + y;
100.00 ¦     lea    (%rdi,%rsi,1),%eax
       ¦   }
       ¦   ? retq
[...]
       ¦            int z = add(x, y);
  1.93 ¦    ? callq  add(int const&, int const&) [clone .isra.0]
       ¦            sum += z;
 79.79 ¦      add    %eax,%ebx

对于fno-align-*

 604,072,552 stalled-cycles-frontend   #    0.00% frontend cycles idle
       9,508 cache-misses
 0.375681928 seconds time elapsed
 [...]
 82.58%  a.out  a.out              [.] work(int, int)
 16.83%  a.out  a.out              [.] add(int const&, int const&) [clone .isra.0]
 [...]
       ¦   __attribute__((noinline))
       ¦   static int add(const int& x, const int& y) {
       ¦       return x + y;
 51.59 ¦     lea    (%rdi,%rsi,1),%eax
       ¦   }
[...]
       ¦    __attribute__((noinline))
       ¦    static int work(int xval, int yval) {
       ¦        int sum(0);
       ¦        for (int i=0; i<LOOP_BOUND; ++i) {
       ¦            int x(xval+sum);
  8.20 ¦      lea    0x0(%r13,%rbx,1),%edi
       ¦            int y(yval+sum);
       ¦            int z = add(x, y);
 35.34 ¦    ? callq  add(int const&, int const&) [clone .isra.0]
       ¦            sum += z;
 39.48 ¦      add    %eax,%ebx
       ¦    }

对于-fno-omit-frame-pointer

 404,625,639 stalled-cycles-frontend   #    0.00% frontend cycles idle
      10,514 cache-misses
 0.375445137 seconds time elapsed
 [...]
 75.35%  a.out  a.out              [.] add(int const&, int const&) [clone .isra.0]                                                                                     ¦
 24.46%  a.out  a.out              [.] work(int, int)
 [...]
       ¦   __attribute__((noinline))
       ¦   static int add(const int& x, const int& y) {
 18.67 ¦     push   %rbp
       ¦       return x + y;
 18.49 ¦     lea    (%rdi,%rsi,1),%eax
       ¦   const int LOOP_BOUND = 200000000;
       ¦
       ¦   __attribute__((noinline))
       ¦   static int add(const int& x, const int& y) {
       ¦     mov    %rsp,%rbp
       ¦       return x + y;
       ¦   }
 12.71 ¦     pop    %rbp
       ¦   ? retq
 [...]
       ¦            int z = add(x, y);
       ¦    ? callq  add(int const&, int const&) [clone .isra.0]
       ¦            sum += z;
 29.83 ¦      add    %eax,%ebx

看起来我们add()在缓慢的情况下无法继续拨打电话。

我已经研究过的一切perf -e可以吐出我的机器上; 不只是上面提供的统计信息。

对于同一可执行文件,stalled-cycles-frontend显示与执行时间成线性关系;我没有发现其他任何可以如此清晰地相互关联的东西。(比较stalled-cycles-frontend不同的可执行文件对我来说没有意义。)

我将高速缓存未命中作为第一条评论包括在内。我检查了所有可以在我的计算机上通过perf而不是上面给出的方法衡量的缓存未命中。缓存未命中非常嘈杂,与执行时间几乎没有关联。


36
盲目猜测:这可能是缓存丢失吗?

@ H2CO3这也是我的第一个想法,但是在没有深入阅读和理解OP的问题的情况下,没有足够的鼓励来发表评论。
2013年

2
@ g-makulik这就是为什么我警告这是一个“盲目的猜测” ;-)“ TL; DR”保留给不好的问题使用的原因。:P

3
只是一个有趣的数据点:当我在OS X上用clang编译时,我发现-O3或-Ofast大约是-O的1.5倍。(我没有尝试用gcc进行再现。)
Rob Napier

2
这是相同的代码。仔细看看.L3的地址,未对齐的分支目标非常昂贵。
汉斯·帕桑

Answers:


503

默认情况下,编译器针对“平均”处理器进行优化。由于不同的处理器支持不同的指令序列,因此启用的编译器优化-O2可能会使平均处理器受益,但会降低特定处理器的性能(适用于-Os)。如果在不同的处理器上尝试相同的示例,您会发现其中一些受益于优化,-O2而另一些则更有利于-Os优化。

以下是time ./test 0 0在多个处理器上的结果(报告了用户时间):

Processor (System-on-Chip)             Compiler   Time (-O2)  Time (-Os)  Fastest
AMD Opteron 8350                       gcc-4.8.1    0.704s      0.896s      -O2
AMD FX-6300                            gcc-4.8.1    0.392s      0.340s      -Os
AMD E2-1800                            gcc-4.7.2    0.740s      0.832s      -O2
Intel Xeon E5405                       gcc-4.8.1    0.603s      0.804s      -O2
Intel Xeon E5-2603                     gcc-4.4.7    1.121s      1.122s       -
Intel Core i3-3217U                    gcc-4.6.4    0.709s      0.709s       -
Intel Core i3-3217U                    gcc-4.7.3    0.708s      0.822s      -O2
Intel Core i3-3217U                    gcc-4.8.1    0.708s      0.944s      -O2
Intel Core i7-4770K                    gcc-4.8.1    0.296s      0.288s      -Os
Intel Atom 330                         gcc-4.8.1    2.003s      2.007s      -O2
ARM 1176JZF-S (Broadcom BCM2835)       gcc-4.6.3    3.470s      3.480s      -O2
ARM Cortex-A8 (TI OMAP DM3730)         gcc-4.6.3    2.727s      2.727s       -
ARM Cortex-A9 (TI OMAP 4460)           gcc-4.6.3    1.648s      1.648s       -
ARM Cortex-A9 (Samsung Exynos 4412)    gcc-4.6.3    1.250s      1.250s       -
ARM Cortex-A15 (Samsung Exynos 5250)   gcc-4.7.2    0.700s      0.700s       -
Qualcomm Snapdragon APQ8060A           gcc-4.8       1.53s       1.52s      -Os

在某些情况下,可以通过要求gcc针对特定处理器进行优化来减轻不利的优化的影响(使用options -mtune=native-march=native):

Processor            Compiler   Time (-O2 -mtune=native) Time (-Os -mtune=native)
AMD FX-6300          gcc-4.8.1         0.340s                   0.340s
AMD E2-1800          gcc-4.7.2         0.740s                   0.832s
Intel Xeon E5405     gcc-4.8.1         0.603s                   0.803s
Intel Core i7-4770K  gcc-4.8.1         0.296s                   0.288s

更新:在基于Ivy Bridge的Core i3上,gcc4.6.44.7.34.8.1)的三个版本产生的二进制文件具有明显不同的性能,但是汇编代码只有细微的变化。到目前为止,我还没有这个事实的解释。

汇编自gcc-4.6.4 -Os(在0.709秒内执行):

00000000004004d2 <_ZL3addRKiS0_.isra.0>:
  4004d2:       8d 04 37                lea    eax,[rdi+rsi*1]
  4004d5:       c3                      ret

00000000004004d6 <_ZL4workii>:
  4004d6:       41 55                   push   r13
  4004d8:       41 89 fd                mov    r13d,edi
  4004db:       41 54                   push   r12
  4004dd:       41 89 f4                mov    r12d,esi
  4004e0:       55                      push   rbp
  4004e1:       bd 00 c2 eb 0b          mov    ebp,0xbebc200
  4004e6:       53                      push   rbx
  4004e7:       31 db                   xor    ebx,ebx
  4004e9:       41 8d 34 1c             lea    esi,[r12+rbx*1]
  4004ed:       41 8d 7c 1d 00          lea    edi,[r13+rbx*1+0x0]
  4004f2:       e8 db ff ff ff          call   4004d2 <_ZL3addRKiS0_.isra.0>
  4004f7:       01 c3                   add    ebx,eax
  4004f9:       ff cd                   dec    ebp
  4004fb:       75 ec                   jne    4004e9 <_ZL4workii+0x13>
  4004fd:       89 d8                   mov    eax,ebx
  4004ff:       5b                      pop    rbx
  400500:       5d                      pop    rbp
  400501:       41 5c                   pop    r12
  400503:       41 5d                   pop    r13
  400505:       c3                      ret

汇编自gcc-4.7.3 -Os(在0.822秒内执行):

00000000004004fa <_ZL3addRKiS0_.isra.0>:
  4004fa:       8d 04 37                lea    eax,[rdi+rsi*1]
  4004fd:       c3                      ret

00000000004004fe <_ZL4workii>:
  4004fe:       41 55                   push   r13
  400500:       41 89 f5                mov    r13d,esi
  400503:       41 54                   push   r12
  400505:       41 89 fc                mov    r12d,edi
  400508:       55                      push   rbp
  400509:       bd 00 c2 eb 0b          mov    ebp,0xbebc200
  40050e:       53                      push   rbx
  40050f:       31 db                   xor    ebx,ebx
  400511:       41 8d 74 1d 00          lea    esi,[r13+rbx*1+0x0]
  400516:       41 8d 3c 1c             lea    edi,[r12+rbx*1]
  40051a:       e8 db ff ff ff          call   4004fa <_ZL3addRKiS0_.isra.0>
  40051f:       01 c3                   add    ebx,eax
  400521:       ff cd                   dec    ebp
  400523:       75 ec                   jne    400511 <_ZL4workii+0x13>
  400525:       89 d8                   mov    eax,ebx
  400527:       5b                      pop    rbx
  400528:       5d                      pop    rbp
  400529:       41 5c                   pop    r12
  40052b:       41 5d                   pop    r13
  40052d:       c3                      ret

汇编自gcc-4.8.1 -Os(在0.994秒内执行):

00000000004004fd <_ZL3addRKiS0_.isra.0>:
  4004fd:       8d 04 37                lea    eax,[rdi+rsi*1]
  400500:       c3                      ret

0000000000400501 <_ZL4workii>:
  400501:       41 55                   push   r13
  400503:       41 89 f5                mov    r13d,esi
  400506:       41 54                   push   r12
  400508:       41 89 fc                mov    r12d,edi
  40050b:       55                      push   rbp
  40050c:       bd 00 c2 eb 0b          mov    ebp,0xbebc200
  400511:       53                      push   rbx
  400512:       31 db                   xor    ebx,ebx
  400514:       41 8d 74 1d 00          lea    esi,[r13+rbx*1+0x0]
  400519:       41 8d 3c 1c             lea    edi,[r12+rbx*1]
  40051d:       e8 db ff ff ff          call   4004fd <_ZL3addRKiS0_.isra.0>
  400522:       01 c3                   add    ebx,eax
  400524:       ff cd                   dec    ebp
  400526:       75 ec                   jne    400514 <_ZL4workii+0x13>
  400528:       89 d8                   mov    eax,ebx
  40052a:       5b                      pop    rbx
  40052b:       5d                      pop    rbp
  40052c:       41 5c                   pop    r12
  40052e:       41 5d                   pop    r13
  400530:       c3                      ret

186
明确地说:您实际上是在12种不同平台上测量OP代码的性能吗?(仅以为您会这样做就为+1)
anatolyg

194
@anatolyg是的,我做到了!(并将很快添加更多内容)
Marat Dukhan

43
确实。另一个+1不仅用于理论化不同的CPU,而且可以实际证明这一点。在关于速度的每个答案中,您都不会看到任何东西。这些测试是否在相同的操作系统上运行?(这可能会使结果偏斜……)
usr2564301 2013年

7
@Ali在AMD-FX 6300上-O2 -fno-align-functions -fno-align-loops,时间缩短到0.340s,因此可以通过对齐来解释。但是,最佳对齐方式取决于处理器:某些处理器更喜欢对齐的循环和功能。
Marat Dukhan

13
@Jongware我不知道操作系统将如何显着影响结果;循环永远不会进行系统调用。
阿里

186

我的同事帮助我找到了一个合理的答案。他注意到256字节边界的重要性。他没有在这里注册,所以鼓励我自己张贴答案(并承担所有成名)。


简短答案:

在这种情况下,罪魁祸首是填充物吗?为什么以及如何?

一切归结为对齐。对齐可能会对性能产生重大影响,这就是为什么我们-falign-*首先使用标志的原因。

我已经向gcc开发人员提交了(伪造的)错误报告。事实证明,默认行为是默认情况下,我们将循环对齐到8个字节,但是如果我们不需要填写超过10个字节,则尝试将其对齐到16个字节”。显然,在这种情况下以及在我的机器上,此默认设置都不是最佳选择。Clang 3.4(trunk)使用-O3适当的对齐方式,并且生成的代码未显示此怪异行为。

当然,如果进行了不适当的对齐,则会使情况变得更糟。不必要的/错误的对齐只会无故吞噬字节,并可能增加缓存未命中率等。

它产生的噪声使定时微优化变得不可能。

当我对C或C ++源代码进行微优化(与堆栈对齐无关)时,如何确保这种偶然的幸运/不幸的对齐方式不会受到干扰?

只需告诉gcc进行正确的对齐即可:

g++ -O2 -falign-functions=16 -falign-loops=16


长答案:

在以下情况下,代码将运行缓慢:

  • 一个XX字节边界切口add()在中间(XX是依赖于机器)。

  • 如果对的调用add()必须跳过XX字节边界并且目标未对齐。

  • 如果 add()未对齐。

  • 如果循环未对齐。

前2条在Marat Dukhan友善张贴的代码和结果上显而易见。在这种情况下,gcc-4.8.1 -Os(在0.994秒内执行):

00000000004004fd <_ZL3addRKiS0_.isra.0>:
  4004fd:       8d 04 37                lea    eax,[rdi+rsi*1]
  400500:       c3   

一个256字节边界削减add()正中既不add()也不是循环排列。惊喜,惊喜,这是最慢的情况!

如果发生这种情况gcc-4.7.3 -Os(在0.822秒内执行),则256个字节的边界只会切成一个冷段(但不会循环,也不会add()被切):

00000000004004fa <_ZL3addRKiS0_.isra.0>:
  4004fa:       8d 04 37                lea    eax,[rdi+rsi*1]
  4004fd:       c3                      ret

[...]

  40051a:       e8 db ff ff ff          call   4004fa <_ZL3addRKiS0_.isra.0>

什么都没有对齐,对的调用add()必须跳过256字节边界。此代码是第二慢的代码。

万一gcc-4.6.4 -Os(在0.709秒内执行),尽管没有对齐,对的调用add()不必跳过256字节边界,并且目标恰好相距32字节:

  4004f2:       e8 db ff ff ff          call   4004d2 <_ZL3addRKiS0_.isra.0>
  4004f7:       01 c3                   add    ebx,eax
  4004f9:       ff cd                   dec    ebp
  4004fb:       75 ec                   jne    4004e9 <_ZL4workii+0x13>

这是三者中最快的。为什么256字节边界在他的机器上是特定的,我将由他自己决定。我没有这样的处理器。

现在,在我的机器上,我没有得到这个256字节的边界效果。只有功能和循环对齐在我的计算机上生效。如果我通过了,g++ -O2 -falign-functions=16 -falign-loops=16那么一切都会恢复正常:我总是遇到最快的情况,而且时间不再对-fno-omit-frame-pointer标志敏感。我可以传递g++ -O2 -falign-functions=32 -falign-loops=32或16的任意倍数,代码也不对此敏感。

我在2009年首先注意到,如果我对大小(-Os)而不是速度(-O2或-O3)进行优化,则gcc(至少在我的项目和我的机器上)倾向于生成明显更快的代码,我一直在想从那以后。

可能的解释是,我有一些对对齐敏感的热点,就像本示例中的热点一样。通过弄乱这些标志(传递-Os而不是-O2),偶然地以一种幸运的方式对齐了这些热点,并使代码变得更快。与尺寸优化无关:这是由于偶然原因使热点更好地对准了。从现在开始,我将检查对齐对我的项目的影响。

哦,还有一件事。像示例中所示的那样,如何出现这种热点?像这样的微小功能的内联怎么会add()失败?

考虑一下:

// add.cpp
int add(const int& x, const int& y) {
    return x + y;
}

并在一个单独的文件中:

// main.cpp
int add(const int& x, const int& y);

const int LOOP_BOUND = 200000000;

__attribute__((noinline))
static int work(int xval, int yval) {
    int sum(0);
    for (int i=0; i<LOOP_BOUND; ++i) {
        int x(xval+sum);
        int y(yval+sum);
        int z = add(x, y);
        sum += z;
    }
    return sum;
}

int main(int , char* argv[]) {
    int result = work(*argv[1], *argv[2]);
    return result;
}

并编译为:g++ -O2 add.cpp main.cpp

      gcc不会内联add()

就是如此,无意间创建像OP中的热点一样容易。当然,部分原因是我的错:gcc是出色的编译器。如果将以上代码编译为:g++ -O2 -flto add.cpp main.cpp,即执行链接时间优化,则代码将在0.19s内运行!

(在OP中人为地禁用了内联,因此,OP中的代码要慢2倍)。


19
哇...这绝对超出了我通常要解决的基准异常问题。
Mysticial

@Ali我认为这很有意义,因为编译器如何内联它看不到的内容?这可能就是为什么我们inline在标头中使用+函数定义。不确定LTO在GCC中的成熟程度。我的经验,至少在mingw,是成功还是失败。
greatwolf 2013年

7
我认为是几年前ACM的通讯文章,内容涉及运行相当大的应用程序(perl,Spice等),同时使用不同大小的Linux环境将整个二进制映像一次移位一个字节。我记得通常有15%左右的差异。他们的总结是,许多基准测试结果无济于事,因为未考虑此比对的外部变量。
基因

1
特别为-flto。如果您以前从未使用过它,那将是非常革命性的,从经验上讲:)
underscore_d

2
这是一段精彩的视频,谈论对齐如何影响性能以及如何对其进行性能分析:youtube.com/watch?
time_continue=1&v=r

73

我要添加此后验收,以指出已经研究了对齐对程序(包括大型程序)整体性能的影响。例如,本文(而且我相信,它的一个版本也出现在CACM中)显示了仅链接顺序和OS环境大小的更改如何足以显着改变性能。他们将其归因于“热循环”的对齐。

这篇论文的标题是“在没有做任何明显错误的情况下产生错误的数据!” 他说,由于程序运行环境中几乎无法控制的差异而导致的无意的实验偏差可能使许多基准测试结果变得毫无意义。

我认为您在同一观察中遇到的角度不同。

对于性能至关重要的代码,这对于在安装或运行时评估环境并在关键例程的不同优化版本中选择本地最佳的系统来说是一个很好的论据。


33

我认为您可以获得与您所做的相同的结果:

我抓取了-O2的程序集,并将其所有差异合并到-Os的程序集中,除了.p2align行:

…使用-O2 -falign-functions=1 -falign-jumps=1 -falign-loops=1 -falign-labels=1。我一直在使用这些选项来编译所有内容,这比-O2我每次尝试测量的速度都要快15年。

另外,对于完全不同的上下文(包括不同的编译器),我注意到情况类似:应该“优化代码大小而不是速度”的选项针对代码大小和速度进行优化。

如果我猜对了,这些是用于堆栈对齐的填充。

不,这与堆栈无关,默认情况下会生成NOP,并且-falign-* = 1选项可以防止代码对齐。

根据为什么GCC垫与NOP一起起作用?这样做是希望代码能够运行得更快,但显然,这种优化在我看来是适得其反的。

在这种情况下,罪魁祸首是填充物吗?为什么以及如何?

填充很可能是罪魁祸首。人们认为填充是有必要的,并且在某些情况下很有用,原因是代码通常以16字节的行读取(有关详细信息,请参见Agner Fog的优化资源,具体取决于处理器的型号)。在16字节的边界上对齐函数,循环或标签意味着从统计上增加了包含该函数或循环所需的行数减少的机会。显然,它适得其反,因为这些NOP降低了代码密度,从而降低了缓存效率。在循环和标签的情况下,甚至可能需要执行一次NOP(当执行正常到达循环/标签时,而不是跳转时)。


有趣的是:-O2 -fno-omit-frame-pointer和一样好-Os。请检查更新的问题。
阿里

11

如果您的程序受CODE L1高速缓存的限制,那么针对大小的优化突然就会开始得到回报。

最后一次检查时,编译器不够聪明,无法在所有情况下都解决这一问题。

在您的情况下,-O3可能为两个高速缓存行生成足够的代码,但是-Os可以容纳在一个高速缓存行中。


1
您想打赌这些align =参数与高速缓存行的大小有关吗?
约书亚

我真的不在乎:它在我的机器上不可见。通过传递-falign-*=16标志,一切都恢复正常,一切行为都将保持一致。就我而言,这个问题已经解决。
阿里

7

我绝不是该领域的专家,但是我似乎记得,现代处理器在分支预测方面非常敏感。用于预测分支的算法是(或者至少是在我编写汇编代码的时代)基于代码的几个属性,包括目标的距离和方向。

我想到的场景是小循环。当分支向后移动并且距离不太远时,分支预测针对这种情况进行了优化,因为所有小循环都是以这种方式完成的。当你交换的位置大致相同的规则可能会发挥作用add,并work在生成的代码或当两个位置稍有改变。

就是说,我不知道如何验证这一点,我只是想让您知道这可能是您要研究的内容。


谢谢。我玩了一下:我只能通过交换来加快速度add()work()如果-O2通过的话。在所有其他情况下,代码会因交换而明显变慢。在周末期间,我还使用来分析分支预测/错误预测统计信息,但perf我没有发现任何可以解释这种怪异行为的信息。唯一一致的结果是,在较慢的情况下,在循环中调用to之后,立即在行上perf报告100.0 in add()并有一个较大的值add()。看起来我们由于某种原因add()在慢速情况下停滞了,但在快速运行中却没有。
阿里

我正在考虑在我的一台计算机上安装Intel的VTune并自行进行性能分析。perf仅支持有限数量的东西,也许英特尔的东西在自己的处理器上更方便。
阿里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.