常见的C ++优化技术列表

时间:2009-03-17 12:20:33

标签: c++ c optimization gcc g++

我可以获得一系列常见的C ++优化实践吗?

我的优化意味着您必须修改源代码才能更快地运行程序,而不是更改编译器设置。

18 个答案:

答案 0 :(得分:24)

我会回应其他人所说的:在性能提升方面,更好的算法将会获胜。

那就是说,我从事图像处理,作为一个问题领域可能会更加坚固。例如,很多年前我有一大堆代码看起来像这样:

void FlipBuffer(unsigned char *start, unsigned char *end)
{
    unsigned char temp;

    while (start <= end) {
        temp = _bitRev[*start];
        *start++ = _bitRev[*end];
        *end-- = temp;
    }
 }

将1位帧缓冲区旋转180度。 _bitRev是256字节的反转位表。这段代码就像你能得到的一样严密。它采用8MHz 68K激光打印机控制器,大约需要2.5秒才能完成一张合法大小的纸张。为了省去细节,客户无法忍受2.5秒。解决方案是与此相同的算法。不同之处在于

  1. 我使用128K表并使用单词而不是字节进行操作(68K在单词上更快乐)
  2. 我使用Duff的设备展开循环,尽可能多地适应短分支
  3. 我进行了优化以跳过空白字
  4. 我终于在汇编中重新编写它以利用sobgtr指令(减去一个并在更大的分支上)并在正确的位置进行“自由”后增量和预减量。
  5. 所以5x:没有算法改变。

    重点是您还需要了解您的问题域以及瓶颈意味着什么。在图像处理中,算法仍然是王道,但如果你的循环正在做额外的工作,那么将这项工作乘以数百万,这就是你支付的价格。

答案 1 :(得分:17)

编写更好程序的两种方法:

充分利用语言

  1. Steve McConnell完成的代码
  2. 有效的C ++
  3. 特殊C ++
  4. 描述您的应用

    1. 确定哪些代码区域占用了多少时间
    2. 看看你是否可以使用更好的数据结构/算法来提高速度
    3. 没有太多的语言特定优化可以做 - 它仅限于使用语言结构(从#1学习)。主要好处来自上面的#2。

答案 2 :(得分:12)

不要忘记一些事情:
- “我们应该忘记小的效率,大约97%的时间说:过早的优化是所有邪恶的根源。” (c)Donald Knuth
- 如果我们优化算法而不是代码,我们可以得到更多 - 我们只会优化现有代码的慢速部分,这些代码将由分析器或其他特殊工具检测。

答案 3 :(得分:9)

Agner Fog在分析几个关于C ++结构的编译器的输出方面做得很好。 你会在这里找到他的作品:http://www.agner.org/optimize/

英特尔也提供了一个很棒的文档 - “英特尔®64和IA-32架构优化参考手册”,您可以在http://www.intel.com/products/processor/manuals/index.htm找到它。虽然它主要针对IA-32架构,但它包含可应用于大多数平台的一般建议。显然,它和Agner Fog的指南确实相交了一些。

正如其他答案中所提到的,在分析和算法选择之后,微优化显然是您想要使程序更快的最后一步。

答案 4 :(得分:5)

您可能对此感兴趣:Optimizing C++ Wikibook

答案 5 :(得分:4)

我没有一个站点,但Sutter的“Exceptional C ++”一书对于C / C ++开发来说非常棒。我强烈建议每个C ++程序员阅读本书,因为它不仅可以优化,而且可以智能地使用该语言,因此您可以进行真正特殊的编程。

答案 6 :(得分:3)

在其他工程学科中,将预算分配给系统组件是很常见的。例如,VTOL飞机的发动机设计为提供一定量的升力,因此重量必须在一个限度内。在较高的水平,飞机的每个部分都有一部分应该满足的重量预算。

这是自上而下完成的原因,而不是等到它过于臃肿才能脱离甲板然后称重每个部件并从最重的位上锉掉一点,部分原因是更换制造部件的成本。但很大一部分原因是,如果您创建的系统中的所有内容都超出预算,那么您无法在一个地方修复它。

典型的软件示例是SGI Indy Irix 5.1,这部分是图形密集型用户现在拥有Mac和Windows机器而不是SGI盒的原因。

“对5.1性能最可怕的是,没有人确切知道它的去向。如果你开始四处询问,你会得到充足的指责和理论,但很少有事实。在5月的报告中,我提出了一个“5%理论”,它指出我们添加的每个小东西(Motif,国际化,拖放,DSO,多种字体等)大约占机器的5%。在15或20之后,大多数表现都消失了。“

通常在讨论性能时,5%被认为是微不足道的,建议是等到出现问题然后寻找单个瓶颈。对于大型系统,等到遇到问题可能会让您失去主要业务。

答案 7 :(得分:2)

++ p通常比p ++快,而-p比p--快,特别是对于前缀和后缀增加和减少运算符的类型的对象,因为前缀形式只是增加或减少某些东西并返回新值,而后缀形式增加或减少一些东西,但必须保持旧值在某处返回它。也就是说,而不是(在这里用你喜​​欢的类替换int)

for ( int i ( 0); i < x; i++)

总是写

for ( int i ( 0); i < x; ++i)

答案 8 :(得分:2)

您要求提供包含优化智慧的网站/来源。

有人提出了一些好的建议。

我可以补充一点,他们几乎都会说分析是最好的,如果不是找到性能问题的唯一方法。

我不确定这种民间智慧起源于何处以及它是如何被证明的,但是there is a better way

增加:

“错误的算法”确实可以扼杀性能,但这肯定不是唯一的方法。

我做了很多性能调整。在大型软件上,通常会导致性能下降的是数据结构过多和抽象层次太多。

看起来像无辜的单行方法调用抽象对象会诱使你忘记那些调用可能会花费你的东西。将这种趋势乘以几个抽象层,你会发现,例如,当带索引的简单数组足够(并且不易维护)但不那么“正确”时,花费所有时间来分配和收集迭代器和集合类之类的东西。 ”

这是“共同智慧”的问题。它往往与智慧完全相反。

答案 9 :(得分:2)

大多数技术都是编译器 - 特定的,因为不同的编译器会以不同的方式进行优化。

如果您想要一些与编译器无关的优化提示,请参阅以下两个:

  1. 不要这样做。
  2. (仅限专家!):不要这样做。
  3. (向Michael A. Jackson道歉)

答案 10 :(得分:1)

  1. 描述您的代码以找到实际花费最多时间的内容
  2. 确保使用正确的算法
  3. 如果您正在进行大量的数字运算,请对缓存友好,并尝试一次性处理大量数据,这样就不必多次加载到缓存中。

答案 11 :(得分:1)

大多数优化都与语言无关。理解您的代码,了解您正在运行的硬件,并且您可以进行大多数低级优化。

了解您的问题域和合适的算法,您可以进行任何高级优化。

我能想到的唯一一个特定于C ++的优化建议是“理解你的代码意味着什么”。理解C ++何时复制临时文件,了解何时调用哪些构造函数和析构函数。

更喜欢函子指针的函数,因为前者可以由编译器内联。通常,尽可能多地移动到编译时而不是运行时。使用模板来完成繁重的工作。

当然,不要尝试优化,直到您分析并说明1)优化是必要的,并且2) 需要优化。

修改的: 评论询问有关函子指针和函数指针的内联。这是解释:

函数通常是独立编译的。那么编译器对函数指针FP作为参数的函数F有何了解?没有什么,它必须查找调用F的地方,并且也许那里它可以找到关于FP指向哪个函数的确定线索。 如果它可以确定从这里调用时,FP将总是指向函数G,然后是,它可以创建一个内联版本的F,其中内嵌G,对于这个特定的呼叫站点。但它不能简单地内联G而不内联F,因为F也可以从其他地方调用,在那里传递一个不同的函数指针。即使这样,它也需要一些昂贵的全局优化来确定任何内容都可以内联。

想象一下你传递了一个像这样的仿函数:

struct Ftor {
  void operator()() { ... }
};

所以函数F看起来像这样:

void F(const FTor& ft) {
  ...
  ft();
  ...
}

现在编译器知道 exacty 调用哪个函数:函数中的第2行调用Ftor :: operator()。因此,呼叫可以很容易地进行内联。

当然,在实践中,您通常会对其进行模板化,因此可以使用任何仿函数类型调用该函数:

template <typename F>
void F(const F& ft) {
  ...
  ft();
  ...
}

答案 12 :(得分:1)

对不起,我没有任何参考资料,但我还有另外一些轶事可以加入。

我使用Microsoft的CString对象作为密钥生成了一个相当大的std :: map。表现是不可接受的。由于我的所有字符串长度都相同,因此我在一个老式的固定大小的字符数组周围创建了一个类包装器,以模拟CString的接口。不幸的是我不记得确切的加速,但它很重要,并且由此产生的性能绰绰有余。

有时您需要了解一些您所依赖的库构造。

答案 13 :(得分:1)

模板元编程可用于从动态多态转移到编译时多态,在过程中生成疯狂的最佳代码。 Alexandrescu的Modern C++ Design是一本很好的书,涵盖了TMP的深度。并非每个页面都是关于优化的,但它在程序设计中经常被考虑。

答案 14 :(得分:1)

与许多人所说的相反,您可以执行许多特定于语言的优化。 This is an excellent resource on Wikibooks。在设计代码时,请记住这一点,然后是个人资料,个人资料,个人资料。

答案 15 :(得分:0)

以下是一对用于优化的所有路径。

优化问题没有一种方式......他们总是手动调整到硬件/软件/系统注意事项。


假设你有最好的算法:

  1. 使用“show assembly output”和“最高优化”进行编译
  2. 查看程序集输出
  3. 识别破坏编译器优化或错误缓存的低效率
  4. 重新编码代码段
  5. 如果仍然不好循环回到2。
  6. 完成
  7. 此处显示的示例:What is the fastest way to swap values in C?


    一般提示:

    http://www.ceet.niu.edu/faculty/kuo/exp/exp6/figuree6-1.jpg

    • 首先尝试浮点
    • 在固定点第二次尝试
    • 如果你真的完全不同并且有很多时间和金钱,那就试试装配

    http://www.asc.edu/seminars/optimization/fig1.gif

    • 检查是内存还是I / O或CPU绑定
    • 攻击永远是限制因素

答案 16 :(得分:0)

可以获得的最佳优化是重新审视设计,并在分析应用程序的性能相关部分/算法之后。这通常不是语言特定的。

我的意思是(仅作为一个想法)如果通过选择稍好的算法(或集合/容器类)来获得30%的性能提升,那么与C ++相关的重构可以获得的改进最多为2% 。设计改进可以为您提供超过30%的任何东西。

如果您有具体的应用程序,最好的策略是测量和分析应用程序。分析通常可以最直接地了解哪些部分与性能相关。

答案 17 :(得分:0)

您可以做很多事情来优化C ++代码。上面列出了一些更广泛的建议。

几个具体的例子是:

  1. 使用数组结构而不是结构数组(DOP与OOP的经典示例)
  2. 当您知道两个指针将不会指向相同的内存位置时,在C ++中使用 restrict

通常,遵循基本的编程范例(例如面向数据的编程)将产生更高的性能,因为DOP是专门针对性能而制定的(以各种形式:内存布局,缓存一致性,运行时成本等)

此处有更多信息:https://people.cs.clemson.edu/~dhouse/courses/405/papers/optimize.pdf