【问题标题】:Are there any drawbacks to using -O3 in GCC?在 GCC 中使用 -O3 有什么缺点吗?
【发布时间】:2016-03-18 17:57:53
【问题描述】:

我从事各种语言的软件工程师已有 13 年了,但我现在才刚刚进入 C 和后来的 C++ 领域。当我学习 C 时,我正在使用 GCC 编译器来编译我的程序,我想知道使用 -O3 或其他优化标志是否有任何问题。是否有可能我的软件会出现在不测试编译代码的情况下无法捕捉到的问题,或者在交叉编译期间,我可能会无意中为不同的平台搞砸了。

在我盲目地启用这些选项之前,我想知道我能期待什么。此外,当 -Ofast 打开不符合标准的标志时,我倾向于不使用它。我的假设是否正确,即 -Ofast 很可能会产生“副作用”?

在发布这个问题之前,我已经浏览了https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

【问题讨论】:

  • 我认为是 -O2 的里程比 -O3 多很多。我建议您确保测试二进制文件...
  • 无论如何你都应该测试所有的二进制文件...
  • @dwelch 你是对的。在我写了关于测试的内容之后,我意识到我无法在它编译之前对其进行测试。我想我已经习惯了可以避开公共界面的脚本语言测试。
  • 如果你利用未定义的行为,你很可能会得到一些惊喜。对于兼容代码:代码的行为不得不同。但是,如果没有代码审查,您的问题将无法回答(这与 SO 无关)。
  • 如果你还想调试你的程序,你应该使用-Og。但是,如果您的代码因优化而中断,您很可能在下一个版本的 gcc 或使用不同的架构、编译器、添加另一行代码等时出现错误行为。依赖 UB 总是招致灾难。

标签: c++ c gcc


【解决方案1】:

“我想知道我能期待什么”

二十多年来,我一直在嵌入式系统中使用 C++(主要是 vxWorks 上的 GCC)。我非常尊重编译器作者。


相信你的编译器:恕我直言,-O3 从未破坏过任何代码……但有时会发现有趣的编码错误。


选择:无论选择 -O1 还是 -O3,团队都必须选择是否“交付您测试的内容,并测试您发布的内容”。我合作过的团队一直致力于使用 -O3 进行发布和测试。


单步可能不合作:在个人实践层面,当使用 -O3 代码时,我通常会“放弃”gdb 单步。我更多地使用断点,并且编码选择略有不同,以使自动变量(堆栈数据)和类数据更“可见”。 (你可以让 gdb 命令 'p' 成为你不方便的朋友)。


单步是必要的:请注意,即使我们使用 -O3 “测试和交付”d,我们几乎只使用 -O1 代码进行调试。


调试是必要的:调试 -01 和测试和交付 -O3 之间的权衡是切换两个可执行文件所需的额外重新编译。在 -O1 中节省的用于探索、识别和修复代码错误的时间必须弥补 2 次重建(到 -01 和返回到 -O3)。


回归测试自动化:我想说 -O3 的系统测试(又名集成测试或回归测试)必须提高一个档次,但我无法真正描述它......也许我应该推荐这个级别测试自动化程度更高(每一个回归测试!)。但我不确定。回归测试的自动化水平可能更多地与团队规模相关,而不是与绩效水平相关。


“成功”的嵌入式系统会做两件事。它满足要求。而且,我认为更重要的是,在所有人类可见的行为中,它就像一个轻载的桌面。对于任何操作(按钮按下、电缆断开、测试设备引起的错误,甚至是轻微的状态灯变化),结果都没有人为可察觉的延迟。 -O3 有帮助。可以做一个成功的系统……我已经看到了。

【讨论】:

    【解决方案2】:

    请务必记住,几乎所有编译器优化都是启发式。换句话说,“优化”只是尝试让您的程序更“优化”,但它很可能会产生相反的效果。仅仅因为-O3 应该比-O2 更好,而-O2 应该比-O1 更好——这并不意味着它实际上总是这样工作的。

    -O2-O1 生成的版本相比,-O3 启用的“优化”有时实际上可能会减慢您的程序速度。您应该尝试不同级别的优化,以查看最适合您的特定代码的方法。如果您真的想对其进行微调,您甚至可以打开和关闭个别优化。

    简而言之——是的,使用-O3 可能有缺点。我个人观察到,我写的很多东西使用-O2 比使用-O3 效果更好——但这确实是特定于程序的。


    仅供参考,这是另一个 SO 问题,询问为什么 -O2-O3 对特定程序提供更好的结果:gcc optimization flag -O3 makes code slower then -O2

    【讨论】:

      【解决方案3】:

      -O3 的唯一缺点应该是无法在调试器中跟踪代码。

      使用-Ofast 可能会影响您的一些浮点运算,从而导致舍入错误,但除非您运行特别长的浮点计算链,否则您不太可能注意到。

      损坏的代码(带有错误指针的代码或具有未定义行为的语句)在不同的优化级别可能表现不同——许多程序员的第一反应是责怪编译器——启用所有警告并修复它们通常会有所帮助。

      【讨论】:

      • 警告和错误默认不启用?太糟糕了。我认为我为此使用了`-Wall`和`-Eall`?我刚刚找到How to turn on (literally) ALL of GCC's warnings?
      • 与您所相信的相反-Wall 不会启用所有警告,而只会启用最有用的子集——您可以使用更多警告check here
      • @RickMacGillis -Wall -Wextra -pedantic 很方便,还记得手动指定语言标准(-std=c11-std=c++14),否则它会给你 GNU 模式和一堆(有用,但非标准)扩展。
      • @Leushenko:扩展通常不应干扰合规代码。
      • -Wconversion 也是值得推荐的。但请注意,如果您的代码没有仔细编写,它可能会引发相当多的警告。但是这些警告非常适合指出潜在的转换问题。
      【解决方案4】:

      自从 -O3 开始移动您的代码以优化它,在某些情况下,您可能会看到结果不同或中断。

      如果您使用 -O3 测试代码的正确性并发现无法调试的问题,建议切换到 -O0 以查看是否得到相同的行为。

      【讨论】:

      • 兼容代码的行为不得与标准要求的不同。除了纯粹的运气之外,更改调试优化不会让您走得更远。 (这与调试优化代码的问题无关;一般来说,调试时应该使用 e-=g - 假设 gcc)。
      • 没错,但并非所有代码都是标准的和/或编写良好的。我特别在具有大量行的科学代码中看到了这种行为。
      • 这就是我从“合规代码...”开始的原因。没有代码审查就无法回答这个问题。
      • 更正:我的意思是 -Og 用于不干扰调试的优化。
      猜你喜欢
      • 2014-10-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-27
      • 2016-09-19
      • 2018-09-01
      • 2012-06-30
      • 2011-04-26
      • 2011-02-28
      相关资源
      最近更新 更多