【问题标题】:g++ -O3 optimizes better than -O2 with all extra optimizations added [duplicate]g++ -O3 比 -O2 优化得更好,添加了所有额外的优化[重复]
【发布时间】:2014-10-14 13:36:20
【问题描述】:

这是我正在查看的函数:

template <uint8_t Size>
inline uint64_t parseUnsigned( const char (&buf)[Size] )
{
  uint64_t val = 0;
  for (uint8_t i = 0; i < Size; ++i)
    if (buf[i] != ' ')
      val = (val * 10) + (buf[i] - '0');
  return val;
}

我有一个测试工具,它通过所有可能的数字,大小为 5,左填充空格。我正在使用 GCC 4.7.2。当我用 -O3 编译后在 callgrind 下运行程序时,我得到:

I   refs:      7,154,919

当我使用 -O2 编译时,我得到:

I   refs:      9,001,570

好的,所以 -O3 提高了性能(我确认一些改进来自上述功能,而不仅仅是测试工具)。但我不想完全从 -O2 切换到 -O3,我想找出要添加的特定选项。所以我咨询man g++ 以获取它说由-O3 添加的选项列表:

-fgcse-after-reload                         [enabled]
-finline-functions                          [enabled]
-fipa-cp-clone                              [enabled]
-fpredictive-commoning                      [enabled]
-ftree-loop-distribute-patterns             [enabled]
-ftree-vectorize                            [enabled]
-funswitch-loops                            [enabled]

所以我再次使用 -O2 编译,然后是上述所有选项。但这给了我比普通 -O2 更差的性能:

I   refs:      9,546,017

我发现将 -ftree-vectorize 添加到 -O2 会导致性能下降。但我不知道如何将 -O3 性能与任何选项组合相匹配。我该怎么做?

如果您想自己尝试,这里是测试工具(将上述parseUnsigned() 定义放在#includes 下):

#include <cmath>
#include <stdint.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>

template <uint8_t Size>
inline void increment( char (&buf)[Size] )
{
  for (uint8_t i = Size - 1; i < 255; --i)
  {
    if (buf[i] == ' ')
    {
      buf[i] = '1';
      break;
    }

    ++buf[i];
    if (buf[i] > '9')
      buf[i] -= 10;
    else
      break;
  }
}

int main()
{
  char str[5];
  memset(str, ' ', sizeof(str));

  unsigned max = std::pow(10, sizeof(str));
  for (unsigned ii = 0; ii < max; ++ii)
  {
    uint64_t result = parseUnsigned(str);
    if (result != ii)
    {
      printf("parseUnsigned(%*s) from %u: %lu\n", sizeof(str), str, ii, result);
      abort();
    }
    increment(str);
  }
}

【问题讨论】:

  • 除了使用 callgrind 之外,你有没有计时?
  • 如果你真的想优化这个函数,基于Size进行dispatch,这样64位算法只在字符串大到足以存储UINT_MAX以上的数字时使用。
  • 不要查看手册页,而是尝试使用this command 来判断哪些选项真正被激活。
  • @BenVoigt:我现在做到了。我将 sizeof(str) 从 5 更改为 9。然后 -O2 需要 11 秒,-O3 需要 6.8 秒,-O2 -ftree-vectorize 需要 13 秒。这是在运行 64 位 Linux 的 2.7 GHz Xeon E5-2697 上。

标签: c++ gcc optimization g++ compiler-optimization


【解决方案1】:

这里已经回答了一个非常相似的问题:https://stackoverflow.com/a/6454659/483486

我已经复制了下面的相关文字。

更新:There are questions about it in GCC WIKI

  • -O1(-O2、-O3 或 -Os)是否等同于单独的 -foptimization 选项?

没有。首先,单个优化选项 (-f*) 不启用优化,需要 x > 0 的选项 -Os 或 -Ox。其次,-Ox 标志启用了许多不受任何单个 -f* 选项控制的优化。 There are no plans to add individual options for controlling all these optimizations.

  • -O1(-O2、-O3 或 -Os)启用了哪些特定标志?

因平台和 GCC 版本而异。你可以让 GCC 告诉你它启用了哪些标志:

touch empty.c
gcc -O1 -S -fverbose-asm empty.c
cat empty.s

【讨论】:

  • 所以你认为如果不扔-O3的厨房水槽就没有办法让GCC更有效地优化这段代码?根据我对这个问题的评论,运行时间的差异并不小——11 秒与 6.8 秒。
  • 基本上,没有优化级别可以在-O2 -faBunchOfStuffThatO3Enables-O3 之间进行选择,而无需根据我在这件事上找到的所有内容编辑编译器的源代码。
  • 你也可以使用-O3 -fno-aBunchOfStuff来缩小差距。并与--param 一起玩(请参阅--help=param 获取列表)。
猜你喜欢
  • 2015-05-06
  • 2018-04-14
  • 2013-01-07
  • 2012-05-13
  • 1970-01-01
  • 2012-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多