【问题标题】:Producing the fastest possible executable生成最快的可执行文件
【发布时间】:2009-11-03 15:26:33
【问题描述】:

我有一个非常大的程序,我一直在 Visual Studio 下编译(v6 然后迁移到 2008)。我需要可执行文件尽可能快地运行。该程序将大部分时间用于处理各种大小的整数,并且只做很少的 IO。

显然我会选择最大优化,但似乎有很多事情可以做,这些事情不在优化的标题下,它们仍然会影响可执行文件的速度。例如选择 __fastcall 调用约定或将结构成员对齐设置为大数字。

所以我的问题是:我应该使用其他编译器/链接器选项来使程序更快,这些选项不受“属性”对话框的“优化”页面的控制。

编辑:我已经广泛使用分析器。

【问题讨论】:

  • 您尝试过其他编译器吗?我听说英特尔 C++ 编译器有时会生成更快的代码。也许值得一试。
  • 我确实在大约一年前尝试过 intel 编译器,它生成的代码与 microsoft 的速度差不多……虽然我可能不知道如何设置所有最大速度的选项。如果我听到很多关于它明显更快的报告,或者有人说“你用标志 X 尝试过吗?这使它运行得更快”或类似的东西,我会再试一次。
  • 在特定的 cpu/平台上是否需要更快?还是尽可能快地跨多个平台(同时接受总有权衡取舍)?
  • 它是一种商业产品,平均可以在高端家用 PC 上运行。

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


【解决方案1】:

另一个需要考虑的优化选项是优化大小。由于更好的缓存局部性,有时大小优化的代码比速度优化的代码运行得更快。

此外,除了优化操作之外,在分析器下运行代码并查看瓶颈在哪里。花时间使用一个好的分析器可以在性能上获得巨大的收益(特别是如果它提供有关代码的缓存友好性的反馈)。

最终,您可能永远不会知道“尽可能快”是什么。你最终需要接受“这对我们的目的来说已经足够快了”。

【讨论】:

  • +3(如果可能的话)100% 同意每一点。你打败了我评论它的大小方面。很多人在优化时都没有考虑到这一点。
  • +1 - 由于内存访问时间的高度差异,现在的一般建议是全局优化大小,只优化速度才是真正的瓶颈。一个循环太长以适应代码缓存可能会对您造成 10 倍的伤害,针对大小而不是速度优化的瓶颈会伤害您 1.alittle
  • 那么为速度和大小设置同等优先级不是一个好主意吗?
  • 仅供参考我尝试优化大小而不是速度,它似乎对我的程序的性能没有影响。这并不是说它不适用于其他人的程序。
【解决方案2】:

Profile-guided optimization 可以带来很大的加速。我的应用程序使用 PGO 构建的运行速度比正常的优化构建快 30%。基本上,您只需运行一次应用程序并让 Visual Studio 对其进行分析,然后根据收集的数据进行优化并再次构建它。

【讨论】:

    【解决方案3】:

    1) 使用__restrict 减少别名。

    2) 使用__pure 帮助编译器进行公共子表达式消除/死代码消除。

    3) 关于 SSE/SIMD 的介绍可以在 herehere 找到。互联网上并没有充斥着有关该主题的文章,但已经足够了。有关内部函数的参考列表,您可以在 MSDN 中搜索“编译器内部函数”。

    4) 对于“宏并行化”,您可以尝试OpenMP。它是用于简单任务并行化的编译器标准——本质上,您使用少量#pragmas 告诉编译器代码的某些部分是可重入的,编译器会自动为您创建线程。

    5) 我赞同 interjay 的观点,即 PGO 非常有用。而且与 #3 和 #4 不同,添加起来几乎毫不费力。

    【讨论】:

      【解决方案4】:

      您是在询问哪些编译器选项可以帮助您加快程序的速度,但这里有一些通用的优化技巧:

      1) 确保您的算法适合这项工作。如果您编写 O(shit squared) 算法,再多摆弄编译器选项也无济于事。

      2) 编译器选项没有硬性规定。有时会针对速度进行优化,有时会针对大小进行优化,并确保计算差异!

      3) 了解您正在使用的平台。了解该 CPU 的缓存如何运行,并编写专门利用硬件的代码。确保您没有随处跟随指针来访问会破坏缓存的数据。了解您可用的 SIMD 操作并使用内在函数而不是编写程序集。仅当编译器肯定没有生成正确的代码时才编写程序集(即以不良方式写入未缓存的内存)。确保在不会别名的指针上使用 __restrict。一些平台更喜欢您通过值而不是通过引用传递向量变量,因为它们可以位于寄存器中 - 我可以继续这样做,但这应该足以为您指明正确的方向!

      希望对你有帮助,

      -汤姆

      【讨论】:

        【解决方案5】:

        忘记您所描述的微优化。通过探查器运行您的应用程序(Visual Studio 中包含一个,至少在某些版本中)。分析器会告诉您应用程序在哪里花费时间。

        微优化很少会给您带来超过几个百分点的性能提升。为了获得真正的巨大提升,您需要确定代码中使用低效算法和/或数据结构的区域。专注于那些,例如通过改变算法。分析器将帮助识别这些问题区域。

        【讨论】:

          【解决方案6】:

          检查您使用的是哪种 /precision 模式。每一个都会生成完全不同的代码,您需要根据应用程序所需的准确性进行选择。我们的代码需要精度(几何、图形代码),但我们仍然使用 /fp:fast(C/C++ -> 代码生成选项)。

          同时确保您拥有 /arch:SSE2,假设您的部署涵盖所有支持 SSE2 的处理器。这将导致性能上有很大差异,因为编译将使用很少的周期。详细信息在博客 SomeAssemblyRequired

          中有很好的介绍

          由于您已经在进行分析,如果没有发生,我建议您展开循环。我见过 VS2008 没有更频繁地这样做(模板、参考等)

          如果适用,在热点中使用 __forceinline。

          更改代码的热点以使用 SSE2 等,因为您的应用似乎计算密集。

          【讨论】:

            【解决方案7】:

            在大多数情况下,在依靠编译器优化来获得显着改进之前,您应该始终解决您的算法并对其进行优化。

            您也可以将硬件用于解决问题。您的 PC 可能已经拥有大部分未使用的必要硬件:GPU!提高某些类型计算量大的处理性能的一种方法是在 GPU 上执行它。这是特定于硬件的,但 NVIDIA 提供了一个 API:CUDA。使用 GPU 可能会比使用 CPU 获得更大的改进。

            【讨论】:

              【解决方案8】:

              我同意每个人对分析的看法。但是,您提到“各种大小的整数”。如果您对不匹配的整数进行大量算术运算,则可能会浪费大量时间来更改大小,例如在计算表达式时将其缩短为整数。

              我还要再补充一件事。可能最重要的优化是选择和实施最佳算法。

              【讨论】:

                【解决方案9】:

                您可以通过三种方式加速您的应用程序:

                1. 更好的算法 - 您没有指定算法或数据类型(整数大小是否有上限?)或您想要的输出。

                2. 宏并行化 - 将任务分成块并将每个块分配给单独的 CPU,因此,在双核 cpu 上,将整数集分成两组并将一半分配给每个 cpu。这取决于您使用的算法 - 并非所有算法都可以这样处理。

                3. 微并行化 - 这与上面类似,但使用 SIMD。您也可以将此与第 2 点结合使用。

                【讨论】:

                • 我认为编译器在最大优化时已经尽可能地使用了 SIMD 指令。没有?
                • 视情况而定。如果您的编译器选项正确,那么简单的东西(如对整数数组求和)可能会优化为 SIMD。如果它是一个更复杂的算法,可能性可能会降低。找出答案的唯一方法是查看编译器生成的代码。
                • 不一定。虽然大多数编译器都可以做一些自动矢量化,但考虑到自动矢量化并不是一件容易的事情,这种支持在体裁上有点初级。所以我不会依赖编译器来模拟最明显的场景
                • 你能给我指出一篇关于如何自己“模拟”代码的文章吗?...我需要说汇编吗?
                • 需要汇编程序来了解 SIMD 扩展的工作原理。编译器可能具有 SIMD 指令的内在函数,因此您可以使用 C 风格的函数调用插入所需的指令(尽管对于不熟悉它的人来说命名非常晦涩)。
                【解决方案10】:

                你说程序很大。这告诉我它可能在一个层次结构中有很多类。

                我对这种程序的经验是,虽然您可能假设基本结构差不多正确,并且为了获得更好的速度,您需要担心低级优化,很有可能有很多优化机会不是低级的。

                除非程序已经过积极调整,否则可能会有以不同方式完成的中间堆栈操作形式的大规模加速空间。这些通常看起来很无辜,永远不会引起你的注意。它们不是“改进算法”的案例。它们通常是恰好在关键路径上的“好设计”案例。

                • 很遗憾,您不能依靠分析器来找到这些东西,因为它们不是为寻找而设计的。

                This is an example of what I'm talking about.

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2011-11-30
                  • 2015-11-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-09-24
                  • 2018-11-14
                  相关资源
                  最近更新 更多