编译器如何优化我们的代码?

12

当我回答另一个人的问题时,遇到了这个问题。编译器如何优化代码?像const这样的关键字是否有帮助?除了使用volatile和inline函数以及如何自己优化代码之外。


2
这是我在stackoverflow的第二天,但我已经看到了第三次有关const优化的话题了...为什么程序员对优化如此着迷,尤其是在大多数情况下并不需要呢?这肯定是一个重复的问题。 - Armen Tsirunyan
非常有趣的问题。试着阅读http://en.wikipedia.org/wiki/Compiler_optimization。 - usr-local-ΕΨΗΕΛΩΝ
@Armen - 可以问一下您为什么认为优化是“不必要的”吗? - linuxuser27
@linuxuser27- 如果你正在创建一个加法器应用程序,计算1+1,你真的需要进行优化吗?答案将在眨眼之间显示出来- 这就是廉价计算器所做的- 使用最慢的处理器仍然可以在不可察觉的时间内得出答案。当然,在某些情况下是需要优化的,只是大多数情况下并不需要。我确实相信优化是一些程序员喜欢做的事情。 - Dominic K
6
我认为@Armen的意思是"在大多数情况下,不需要[由程序员引导]优化"。实际上,为问题选择最简单和最干净的方法和算法比试图通过CPU挤出额外的指令要更有益。 - GManNickG
显示剩余8条评论
3个回答

13

编译器可自由优化代码,只要能保证代码的语义不变。

我建议从编译器优化维基百科页面开始,因为有许多不同类型的优化在许多不同阶段执行。

正如您所看到的,现代编译器非常“聪明”,可以优化代码(编译的C代码通常比手写汇编代码更快,除非程序员真的知道如何利用所有特定处理器指令和怪癖)。就像其他人所说的,首先基于良好的设计编写清晰易懂的代码。


编译器可以在知道代码运行的硬件时进行优化!但有时候当GPU介入时,会出现问题。最近我写了一段同时使用CPU和GPU(CUDA)的代码,结果发现问题就是简单的O2优化。当我关闭它时,所有事情都变得清晰明了。 - Amir Zadeh
@绿色代码:编译器也是软件,所以它们有时候会有错误。但对于成熟的编译器来说,输出通常比大多数程序员自己编写的内容正确且运行速度快得多。 - user395760
@绿色代码:有各种级别的优化。有些是机器无关的,有些则不是。机器相关的可能不是没有bug的,特别是对于年轻的架构,因为它们还没有经过广泛测试。CUDA也带来了一个新的困难:突然间代码的某些部分应该针对CPU进行优化,而另一些部分则应该针对GPU进行优化。我所知道的C++编译器中没有一个是为同时优化两种不同体系结构而设计的。 - Matthieu M.
编译器并不是那么聪明。它们甚至不能重新排列一个简单的布尔表达式,比如 (A && B && C && D),以优化短路运算。它们采取保守策略,因为它们无法确定方法是否具有副作用,既然无法确定,就无法安全地重新排列表达式。由于它们没有这样做,它们甚至不费力地在编译时计算操作数复杂度,也不对评估操作符的成本进行分析,以确定哪些操作符最好触发短路,哪些操作符很昂贵,最好被短路(即排在最后)。 - Triynko

6

除了编译器可以为您执行的操作之外,您可以做的一件非常重要的事情是了解缓存。由于访问内存真的很费时间,缓存试图通过存储不仅是您所访问的数据,而且是附近元素来帮助您。这就是为什么foobar运行得更快的原因:

array[ NUM_ROWS ][ NUM_COLS ];

foo() 
{
    int row, col;
    int sum = 0;

    // accesses the elements in the array continuously
    for ( row = 0; row < NUM_ROWS ; row++ ) 
    {
         for ( col = 0; col < NUM_COLS; col++ )
         {
              sum += array[ row ][ col ];
         }
    }
}

bar() 
{
    int row, col;
    int sum = 0;

    // skips from row to row ( big jumps that might miss the cache )
    for ( col = 0; col < NUM_COLS ; col++ ) 
    {
         for ( row = 0; row < NUM_ROWS; row++ )
         {
              sum += array[ row ][ col ];
         }
    }
}

编辑: 另一件需要注意的事情是字符串重复拼接。如果做得不好,这会使看起来运行时间为O(n)的代码实际上变成O(n^2) - 请参阅Joel on Software的文章。

编辑:将“磁盘”替换为“内存”。


4
你的示例代码与访问磁盘有什么关系? - JeremyWeir
4
意思是:了解缓存将为您节省更多的时间,而不仅仅是++row:P@jayrdub: 这需要解释一下内存在计算机中实际是如何工作的。基本上,array[row][col]是对最初存储在硬盘上的主存储器的调用。由于硬盘移动速度比CPU慢得多,因此计算机会将信息存储在一个“缓存”中,以便更容易地访问它。 - Alex Reece
4
我认为你的电脑有些奇怪。 - JeremyWeir
3
@Alex Reece - 我不认为有人这样说过。反对意见是,当你指的是内存时,你说“磁盘”。请改为正确的术语。 - asveikau
3
@Alex: gcc (-floop-interchange) 参见 http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html,我认为在 llvm 中被称为 loop-rotate 的应该就是它:http://llvm.org/docs/Passes.html#loop-rotate。 - Matthieu M.
显示剩余10条评论

-5

优化的规则:

  1. 不要去做
  2. 仅限高级用户:暂时不要去做

编辑: 引用(以及其他有用或无用的信息)可以在CodingHorror文章中找到: 硬件便宜,程序员昂贵。不过找到这个短语/引用的“起源”会很好。


2
问题不在于手动优化,而在于编译器可能会做什么以及它们如何实现。 - DerKuchen
嗨,谢谢你的建议,但我想我已经快掌握它了,所以我不想误解它。 - Amir Zadeh
@DerKuchen:嗯,这涉及到两者。 - Michael Petrotta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接