【问题标题】:Does the VS2008 C++ optimizer sometimes produce slower code?VS2008 C++ 优化器有时会产生较慢的代码吗?
【发布时间】:2011-03-03 11:38:23
【问题描述】:

previous question 开始,我一直在我的发布版本中使用优化器设置,以了解使用编译器优化可以带来哪些好处。到目前为止,我一直在使用 /Ob1(仅在显式给出 inline 的情况下使用 inline)和 /Oi(启用内部函数)。我尝试将其更改为包括 /Ot(支持快速代码)、/Oy(省略帧指针)和 /Ob2(内联任何合适的),令我惊讶的是,一个需要 2 小时 58 分钟的回归套件现在需要 3 小时 16 分钟。我的第一个假设是我自己的内联比编译器更具侵略性,但是从 /Ob2 回到 /Ob1 只会将事情改进到 3h12m。我仍在运行更多测试,但似乎在某些情况下 /Ot (喜欢快速代码)实际上会减慢速度。该软件是多线程和计算密集型的(表面建模、操作和可视化),并且已经根据分析器结果进行了大量手动优化。该程序还处理大量数据,并经常使用#pragma pack(4)。

所以问题是这样的。对于手动优化的程序,VS2008 中的编译器优化是否弊大于利?换句话说,是否存在编译器优化会降低性能的已知记录场景? (n.b. 分析编译器优化的代码很痛苦,因此迄今为止已经对未优化的代码进行了分析。

编辑根据 Cody Gray 和其他人的建议,我已将 /O2 添加到优化设置中并重新执行了我的测试套件。这导致运行时间为 3h01,与最低限度优化的运行相当。鉴于(略微过时)MSDN guide lines on optimization 和来自 GOZ 的帖子,我将检查 /O1 以查看在我的情况下是否更小实际上更快。请注意,当前的 EXE 文件约为 ~11mb。我还将尝试一起构建 VS2010,看看效果如何。

Edit2 使用 /O1,运行时间为 3h00,11mb exe 小 62k。请注意,这篇文章和之前链接的文章背后的原因是检查打开编译器优化的好处是否超过了分析和调试方面的缺点。在这个特定的例子中,它们似乎没有,尽管我承认我很惊讶没有尝试过的组合增加任何好处并且明显降低了性能。 FWIW,根据previous thread,我倾向于在设计时进行大部分优化,并主要使用分析器来检查设计假设,我认为我会坚持使用这种方法。我将在 VS2010 上进行最后一次启用,并启用整个程序优化并保留它。

感谢所有反馈!

【问题讨论】:

  • 有一段时间没用过 VS,所以也许这应该很明显:除了你提到的 epxlicit 优化之外,你编译的优化级别是什么? /O0?这当然是不行的……
  • 虽然分析优化代码可能会很痛苦,但我不确定您是否要禁用优化以便手动分析和优化...不过这可能为时已晚。一般来说,优化器被设计为在常见的习语上工作得最好,你将从那里的优化器中获得最大的改进。然后,您可以集中精力分析/优化可以手动帮助编译器优化的热点
  • @Konrad,在 IDE 中优化设置为自定义,查看命令行选项,我没有设置优化级别,例如/Ob1 /Oi /Ot 是列出的唯一 optimizatopn 开关,当然没有 /O0 或 /Od。
  • Release 版本的默认设置已经将优化提升到了最大值。与翻转 /O2 相比,您几乎无法从应用中挤出更好的性能。我很高兴看到 that 的基准,而不是您的自定义优化设置。
  • @Cody,现在试试。大约 3 小时后立即回复您; )

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


【解决方案1】:

/Otdocumentation 声明:

如果你使用/Os/Ot,那么你还必须指定/Og来优化代码。

因此,您可能希望在测试中始终通过 /Og/Ot

也就是说,/Ot 以牺牲程序大小为代价来支持快速代码,并且可以生成非常大的二进制文件,尤其是在具有大量内联的情况下。大型二进制文件难以利用处理器缓存。

【讨论】:

  • 这是正确的。但我建议不要添加/Og,而是简单地指定/O2 并保留它。我真的很难相信你真的会比 VS 的编译器更聪明。
  • 谢谢 Frédéric,我会试试这个。我很惊讶 IDE 默认不这样做,通过在线帮助说从 VS2005 开始 /Og 是一个已弃用的选项。
  • @Shane:默认情况下它不会这样做,因为默认情况下更好:/O2。他们试图在最近的版本中大幅简化编译器选项,因为确定没有人理解或正确使用它们。 /Og 已被弃用,因为零散设置这些标志并没有真正的好处。
  • @Shane, /Og 已被弃用,有利于 /O1(这并不意味着 /Ot)和 /O2(确实如此)。所以,我会第二个@Cody,并建议你使用其中之一:)
【解决方案2】:

它很可能试图内联大型函数,或者至少是循环中的大量函数。此时,您将面临导致指令缓存重新加载的风险。这可能会导致大幅减速。内联并不总是最好的做法(尽管通常它很有帮助)。如果您有任何包含大量函数调用的大循环,那么最好将循环分成几个循环。这样循环可以留在指令缓存中,并且您可以获得更好的性能。

【讨论】:

  • 虽然您的观点从一般角度来看是正确的,但编译器非常不太可能内联如此大的函数。我从来没有见过它做这么愚蠢的事情。
  • 您是否看到“基于分析器结果进行大量手动优化”的报价?可能会有一堆强制内联与内置的启发式方法一起造成严重破坏。
  • @Cody:那你是一个非常幸运的人。编译器经常做各种迟钝的事情。尽管如此,如果打开“尝试内联所有内容”会产生较慢的代码,我看不出这怎么可能是问题!还值得注意的是,除非您有一个被大量调用的繁重循环,否则您甚至可能不会注意到发生了这样的问题。
  • @MSalters,这也是我最初的结论,但是强制内联和让编译器控制内联之间的差异并不那么显着。
  • @Cody,下面的 MSDN 文章倾向于支持 GOZ 的观点,即 /O2 可能不适合大型应用程序。 msdn.microsoft.com/en-us/library/aa290055(v=vs.71).aspx
【解决方案3】:

众所周知,favour fast code 并不总是比favour small code 快。启发式编译器不是无所不知的,它可能会出错。在某些情况下,更小的代码比更快的代码更快。

使用/O2 获得最快的代码 - 编译器比您更了解各种设置如何交互。

等等。您分析了未优化的代码?那是精神错乱。编译器优化不像手动优化 - 它们总是完成并且没有理由对其进行分析 - 您可以识别不存在的瓶颈等等。如果您想要准确的分析数据,您首先让编译器做到最好,然后然后你配置文件。

您还可以考虑使用 Profile Guided Optimization,它将以一些令人印象深刻的方式指导编译器的优化器。

【讨论】:

  • 当您说“众所周知”时,您能否引用您的参考资料,因为我在搜索后找不到任何参考资料。您关于编译器启发式不是无所不知的陈述似乎与您的以下陈述直接矛盾。至于分析未优化的代码,我正在分析未使用优化器的发布版本,原因有两个。首先,它使分析变得更加困难。其次,之前尝试过的编译器优化带来的性能提升并没有提供足够显着的收益来保证将其包含在内。我主要是为了检查设计假设。
  • “众所周知”意味着一些人已经看到,如果您已经自己调整过代码,要求编译器调整速度可能无济于事。要求它生成小代码有时有助于改善缓存占用空间,并获得百分之几的额外速度。不过还没有看到有人写过关于它的文章。
  • @Bo,听起来像是在为外面的人写一篇好文章。我进行了一些搜索,大部分材料似乎已经过时了。您对“众所周知”的定义,即编译器优化可能并不总是有助于提高性能,这往往与我在这种特定情况下的发现一致,但对于其他人来说,编译器总是最了解的似乎是“众所周知的”,因此请求参考。我怀疑有更多的猜测而不是关于这个主题的最新经验,并且很想被证明是错误的;)
猜你喜欢
  • 1970-01-01
  • 2017-02-20
  • 2010-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-15
  • 2017-05-08
相关资源
最近更新 更多