常见C ++优化技术列表

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

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

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

充分利用语言

  1. 代码由Steve McConnell完成
  2. 有效的C ++
  3. 特殊的C ++

描述你的申请

  1. 确定哪些代码区域占用了多少时间
  2. 看看你是否可以使用更好的数据结构/算法来加快速度

人们可以做的语言没有太多优化 – 它仅限于使用语言结构(从#1学习)。 主要好处来自上面的#2。

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

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

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指令(减去一个并在更大的分支上)并在正确的位置具有“自由”后增量和预减量。

所以5x:没有算法改变。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

我不确定这种民间智慧起源于何处,或者它是如何被certificate的,但是有更好的方法 。

添加:

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

我做了很多性能调整。 在大型软件上,通常杀死性能的是太多的数据结构和太多的抽象层。

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

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

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

如果你想要一些与编译器无关的优化技巧,这里有两个:

  1. 不要这样做。
  2. (仅限专家!):不要这样做。

(向Michael A. Jackson道歉)

++ p通常比p ++快,而-p比p–快,特别是对于具有重载前缀和后缀增量和减量运算符的类型的对象,因为前缀forms只是增加或减少某些东西并返回新值,而postfix表单增加或减少某些东西,但必须保留旧值以返回它。 也就是说,而不是(在这里用你最喜欢的类替换int)

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

总是写

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

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

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

我能想到的唯一一个特定于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(); ... } 

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

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

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

对不起,我没有任何参考资料,但我确实有另一个轶事加入到堆中。

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

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

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

与许多人所说的相反,您可以执行许多特定于语言的优化。 这是Wikibooks的优秀资源 。 在设计代码时,请记住这一点,然后是配置文件,配置文件,配置文件。

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

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

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

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

优化问题没有一种方法……他们总是手动调整到硬件/软件/系统考虑因素。


假设你有最好的算法:

  1. 用“显示汇编输出”和“最高优化”编译
  2. 看一下assembly输出
  3. 识别破坏编译器优化或错误缓存的低效率
  4. 重新编码代码段
  5. 如果还是坏循环回到2。
  6. DONE

这里看到的例子: 在C中交换值的最快方法是什么?


一般提示:

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

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

http://sofzh.miximages.com/c%2B%2B/fig1.gif

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

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

一些具体的例子是:

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

一般而言,遵循基于数据的编程范式(如面向数据的编程)会产生更高的性能,因为DOP专门用于关注性能(所有forms:内存布局,缓存一致性,运行时成本等)

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