【问题标题】:Visual Studio C++ compiler optimizations breaking code?Visual Studio C++ 编译器优化破坏代码?
【发布时间】:2011-04-13 02:23:43
【问题描述】:

我在这里遇到了一个特殊的问题,VS2005 和 2010 都发生了这种情况。我有一个 for 循环,其中调用了一个内联函数,本质上是这样的(C++,仅用于说明目的):

inline double f(int a)
{
  if (a > 100)
  {
    // This is an error condition that shouldn't happen..
  }

  // Do something with a and return a double
}

然后在另一个函数中循环:

for (int i = 0; i < 11; ++i)
{
  double b = f(i * 10);
}

现在发生的事情是在调试构建中一切正常。在打开所有优化的发布版本中,根据反汇编,编译后直接使用i,而没有* 10,比较a &gt; 100变成a &gt; 9,而我猜它应该是@987654327 @。你有什么线索可以让编译器认为a &gt; 9 是正确的方法吗?有趣的是,即使是对周围代码的微小更改(例如调试打印输出)也会使编译器使用 i * 10 并将其与文字值 100 进行比较。

我知道这有点含糊,但如果有任何旧想法,我将不胜感激。

编辑:

这是一个有望重现的案例。我不认为它太大,不能在这里粘贴,所以这里是:

__forceinline int get(int i)
{
  if (i > 600)
    __asm int 3;

  return i * 2;
}

int main()
{
  for (int i = 0; i < 38; ++i)
  {
    int j = (i < 4) ? 0 : get(i * 16);
  }

  return 0;
}

我在我的机器上使用 VS2010 对此进行了测试,它的表现似乎与我遇到问题的原始代码一样糟糕。我在发布配置中使用 IDE 的默认空 C++ 项目模板编译并运行了它。如您所见,决不应该触发中断 (37 * 16 = 592)。请注意,删除 i &lt; 4 会使其正常工作,就像在原始代码中一样。

【问题讨论】:

  • 提供一个可编译并重现问题的最小示例怎么样?
  • 您写道,为优化构建生成的汇编语言不是您所期望的。但并不完全清楚优化后的程序执行不正确。您能否确认确实如此,并告诉我们这两个版本的不同结果是什么?
  • 实际上,在提出这个问题之前,我自己很快就尝试将其作为一个完全独立的案例重现,但到目前为止还没有以相同的方式编译它。我无法复制粘贴原始代码,但我会看看能否获得问题部分的独立非上下文版本。

标签: c++ visual-studio visual-c++ compiler-optimization


【解决方案1】:

对于任何感兴趣的人,结果证明这是 VS 编译器中的一个错误。经 Microsoft 确认并在报告后的服务包中修复。

【讨论】:

    【解决方案2】:

    首先,如果您能发布足够多的代码让我们重现该问题,将会有所帮助。否则你只是要求进行心理调试。

    其次,编译器有时无法在最高优化级别生成有效代码,但更有可能的是,您的代码中存在错误。如果代码中某处存在未定义行为,则意味着优化器所做的假设可能不成立,然后编译器最终可能会生成错误代码。

    但是没有看到你的实际代码,我真的无法更具体。

    【讨论】:

      【解决方案3】:

      我知道的唯一著名的优化错误(并且只有最高优化级别)是偶尔修改操作的优先级顺序(由于优化器执行的操作发生变化,寻找最快的计算方式)。你可以朝这个方向看(并加上一些括号,即使严格来说它们不是必需的,这就是为什么更多的括号永远不会坏的原因),但坦率地说,这类错误非常罕见。

      如前所述,如果没有更多代码,很难有任何精确的想法。

      【讨论】:

        【解决方案4】:

        首先,内联汇编会阻止某些优化,您应该使用 __debugbreak() 内在函数进行 int3 断点。编译器看到内联函数除了断点之外没有任何影响,因此它将 600 除以 16(注意:这受整数截断影响),因此它优化为 debugbreak 以触发 38 > i >= 37。所以看起来为此而努力

        【讨论】:

        • 这是从实际上对返回结果做了一些事情的版本中剥离出来的。即使这样,它也会重现,并使用内联汇编以外的其他东西来表示 i 的值是意外的。不过我可能做错了什么。
        • 这里的问题是600 / 16的整数截断,导致它在37而不是37.75触发。如果函数没有被内联,这不会发生,因为编译器不会预先划分。如果使用浮点数,它可能会正常工作(需要检查)
        猜你喜欢
        • 2016-04-22
        • 1970-01-01
        • 2012-10-24
        • 2021-02-01
        • 2012-02-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-11
        相关资源
        最近更新 更多