【问题标题】:gcc optimizations cause app to failgcc 优化导致应用程序失败
【发布时间】:2011-04-20 18:10:51
【问题描述】:

我在使用 GCC for ARM 并启用了优化时遇到了一个非常奇怪的问题。 在没有优化的情况下编译我的 C++ 应用程序会生成一个可执行文件 在运行时输出预期结果。我一打开 优化 - 即 -O1 - 我的应用程序无法产生预期的结果。 我尝试了几天来发现问题,但我一无所知。 我从我的代码中消除了任何未初始化的变量,我纠正了那些地方 严格的别名可能会导致问题,但我仍然没有正确的结果。

我正在为 ARM 使用 GCC 4.2.0(处理器是 ARM926ej-s)并运行应用程序 在 Montavista Linux 发行版上。

以下是我正在使用的标志:

-O1 -fno-unroll-loops fno-merge-constants -fno-omit-frame-pointer -fno-toplevel-reorder \
-fno-defer-pop -fno-function-cse -Wuninitialized -Wstrict-aliasing=3 -Wstrict-overflow=3 \
-fsigned-char -march=armv5te -mtune=arm926ej-s -ffast-math

只要我去掉 -O1 标志并重新编译/重新链接应用程序,我就会得到正确的输出结果。正如您从标志中看到的那样,我试图禁用任何我认为可能会导致问题的优化,但仍然没有运气。

有人对我如何进一步解决这个问题有任何建议吗?

谢谢

【问题讨论】:

  • 什么是“预期结果”?您是否在选择了错误的“if”分支或计算错误时出现逻辑错误?你确定你所有的变量都正确初始化了吗?
  • 计算错误。是的,我很确定所有变量都已正确初始化。
  • 这就像在说“我很确定我的代码是正确的,为什么它不起作用?” ;)
  • @jalf:不,这就像说“我真的检查过我的所有变量都已正确初始化并具有正确的默认值”。我很确定代码在所描述的特定场景中的其他方面不正确。
  • 我的意思是“相当肯定”并不是真的有用。如果您不确定 100%,那么它仍然是一个不应忽视的潜在错误源。 :) 调试时,只有“绝对确定”和“不确定”;)

标签: c++ gcc compiler-optimization fast-math


【解决方案1】:

-ffast-math 应尽可能避免。现在只需使用-O1 并删除所有其他优化开关。如果您仍然看到问题,那么是时候开始调试了。

【讨论】:

  • +1: -ffast-math 是主要候选人。它允许编译器进行不能保证给出相同结果的浮点替换。
  • +1:但我接受了另一个答案,因为我需要在我的后脑勺上一记耳光
  • 实际上,我的建议通常是相反的:如果打开 -ffast-math 会显着影响程序的结果,那么您可能需要修复数值不稳定的代码(很高兴它很容易找到!)
【解决方案2】:

没有看到您的代码,很难比“您可能有错误”更具体。

启用优化会改变程序的语义有两种情况:

  • 编译器中存在错误,或者
  • 您的代码中存在错误。

后者可能是最有可能的。具体来说,您可能依赖于程序中某处的未定义行为。当您在带有 这些 编译器标志的 this 计算机上使用 this 编译器进行编译时,您所依赖的东西恰好是真的,但事实并非如此t 由语言保证。因此,当您启用优化时,GCC 没有义务保留该行为。

向我们展示您的代码。或者在调试器中单步执行,直到出现问题为止。

我不能再具体了。它可能是一个悬空指针、未初始化的变量、破坏了别名规则,甚至只是做了许多会产生未定义结果的事情之一(如i = i++

【讨论】:

  • 我无法显示代码。你可能是对的,代码中有一个由标志组合触发的错误。我调试了应用程序,但没有发现问题(这当然并不意味着我应该停止调试)......我只是想知道组合标志是否存在已知问题,或者是否有人遇到过这种情况描述。
  • 很多人都遇到过这样的场景。但就像我说的那样,它通常只有在你的代码违反编译器做出的某些假设时才会发生,要么是因为编译器的假设是错误的(有缺陷的编译器),要么是因为你正在做的事情不是由语言定义的(所以编译器做出它所做的假设是正确的,但您的代码只是不满足它们,因为它依赖于未定义的行为。不幸的是,在 C 中您可能会做的很多事情看起来很安全,但实际上是未定义的。
【解决方案3】:

一般来说,如果您说“优化破坏了我的程序”,那么您的程序有 99.9% 被破坏了。启用优化只会发现代码中的错误。

您还应该轻松使用优化选项。只有在非常特殊的情况下,您才需要标准选项 -O0、-O2、-O3 和可能的 -Os 之外的任何其他内容。如果您觉得自己确实需要比这更具体的设置,请注意优化的口头禅:

衡量、优化、衡量。

永远不要在这里按“直觉”行事。证明某个非标准优化选项确实对您的应用程序有很大好处,并了解原因(即,准确了解该选项的作用,以及它为什么会影响您的代码)。

这里不是蒙眼导航的好地方。

看看你如何使用最具防御性的选项 (-O1),然后禁用六个优化,然后然后添加 -ffast-math,这让我假设你目前正在这样做.

嗯,也许是一只眼睛。

但底线是:如果启用优化会破坏您的代码,则很可能是您的代码有问题。

编辑:我刚刚在 GCC 手册中找到了这个:

-ffast-math: 这个选项不应该被任何 -O 选项打开 因为它可能导致依赖于的程序的输出不正确 精确执行 IEEE 或 ISO 数学规则/规范 职能。

这确实表明,您的-O1 -ffast-math 确实可以破坏正确 代码。但是,即使删除-ffast-math 可以解决您当前的问题,您至少应该知道为什么。否则,您可能只是在 现在 用更不方便的时刻 (例如,当您的产品在客户所在地发生故障时)交换您的问题。真的是-ffast-math 是问题所在,还是-ffast-math发现的数学代码损坏了?

【讨论】:

  • 谢谢你的耳光!就是这样。在文档中检查该标志时,我没有看到“这取决于数学函数的 IEEE 或 ISO 规则/规范的精确实现”。
  • 很高兴您找到了问题;但是您仍然应该尝试找出哪些代码对 -ffast-math 敏感(它可能是您使用的库)。敏感代码可能在数值上是不稳定的,并且至少对你没有立即预料到的变化很敏感。
  • @Eamon Nerbonne:你说的完全正确,这就是让我在接下来的几天里忙碌的原因。
【解决方案4】:

尝试做一个最小的测试用例。重写程序,删除不影响错误的东西。您可能会在此过程中自己发现错误,但如果您没有发现,您应该有一个单屏示例程序可以发布。

顺便说一句,如果,正如其他人所推测的那样,是 -ffast-math 导致了您的麻烦(即仅使用 -O1 编译工作正常),那么您可能有一些数学问题无论如何你都应该重写。这有点过于简化了,但-ffast-math 允许编译器从本质上重新排列计算,因为您可以抽象数学数字 - 即使在真实硬件上这样做可能会导致略有不同的结果,因为浮点数不精确。依赖这种浮点细节很可能是无意的。

如果您想了解这个错误,无论如何,一个最小的测试用例是至关重要的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-03
    • 1970-01-01
    • 2019-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多