有人可以指点我一篇文章,或者在这里写一些关于一些C ++编程习惯的技巧,这些习惯通常是有效的(没有真正的缺点)并且可以提高性能吗?我不是指编程模式和算法的复杂性 - 我需要一些小的东西,比如你如何定义你的函数,要做的事情/要避免在循环中,在堆栈上分配什么,堆上的内容等等。
这不是关于如何更快地制作一个特定的软件,也不是关于如何创建一个干净的软件设计,而是关于编程习惯 - 如果你总是应用它们,你将使你的代码比一点点快一点慢。
答案 0 :(得分:32)
Effective C++,More Effective C++,Effective STL和C++ Coding Standards中的一些提示就在这一行。
此类提示的一个简单示例:尽可能使用preincrement(++ i)而不是postincrement(i ++)。这对于迭代器尤为重要,因为后增量涉及复制迭代器。优化器可以能够撤销这个,但是编写预增量不是额外的工作,那么为什么要冒风险呢?
答案 1 :(得分:26)
如果我理解正确,你会问到避免过早的悲观化,这是避免过早优化的一个很好的补充。根据我的经验,要避免的首要任务是尽可能不复制大型对象。这包括:
这最后一个子弹需要一些解释。我不能告诉你我见过多少次:
class Foo
{
const BigObject & bar();
};
// ... somewhere in code ...
BigObject obj = foo.bar(); // OOPS! This creates a copy!
正确的方法是:
const BigOject &obj = foo.bar(); // does not create a copy
这些指南适用于大于智能指针或内置类型的任何内容。另外,我强烈建议您花时间学习分析您的代码。一个好的分析工具将有助于捕获浪费的操作。
答案 2 :(得分:16)
我的一些小小的烦恼:
if (a && b)
,如果b更可能是假的,则将其放在第一位以保存对a的评估。还有许多其他“坏习惯”,我不会提及,因为在实践中,现代编译器/优化器将消除不良影响(例如,返回值优化与传递引用,循环展开等)。
答案 3 :(得分:15)
其中一个好的起点是Sutter's Guru of the week系列,以及 Exceptional C ++ 这些书籍。
答案 4 :(得分:11)
Optimizing Software in C++“Agner Fog”通常是优化技术的最佳参考之一,既简单又绝对更高级。另一个很大的优点是可以在他的网站上免费阅读。 (请参阅他的网站名称中的链接,并链接到pdf的纸质标题)。
编辑另请注意,90%(或更多)的时间花费在代码的10%(或更少)上。因此,一般来说优化代码实际上是针对您的瓶颈。此外,了解现代编译器将比大多数编码器更好地进行优化是非常重要和有用的,尤其是微优化,例如延迟变量的初始化等。编译器通常非常擅长优化,因此花时间编写稳定,可靠的和简单的代码。
我认为,至少在大多数情况下,更多地关注算法的选择而不是微观优化是值得的。
答案 5 :(得分:11)
使用仿函数(实现了operator()
的类)而不是函数指针。编译器可以更轻松地内联前者。这就是为什么C ++的std::sort
往往比C qsort
表现得更好(当给出一个仿函数时)。
答案 6 :(得分:8)
从你的问题看来,你已经知道“过早优化是邪恶的”哲学,所以我不会那么鼓吹。 :)
现代编译器已经非常聪明地为您进行微优化。如果你太努力了,你通常可以比原来的直接代码慢。
对于小的“优化”,你可以不假思索地安全地做,并且不会对代码的可读性/可维护性产生太大影响,请查看本书 C ++编码标准由Sutter& Alexandrescu的。
有关更多优化技巧,请查看Bulka& amp;的 Efficient C ++ 。梅休。仅在通过分析证明合理时使用!
要获得良好的通用C ++编程实践,请查看:
在我的头脑中,一个很好的一般性能练习是通过引用传递重量级对象,而不是通过复制。例如:
// Not a good idea, a whole other temporary copy of the (potentially big) vector will be created.
int sum(std::vector<int> v)
{
// sum all values of v
return sum;
}
// Better, vector is passed by constant reference
int sum(const std::vector<int>& v)
{
// v is immutable ("read-only") in this context
// sum all values of v.
return sum;
}
对于像复数或二维(x,y)点这样的小对象,该函数可能会在副本传递的对象下运行得更快。
对于固定大小的中等重量对象,如果使用副本或对象的引用,函数运行得更快,则不太清楚。只有剖析才能说明问题。我通常只是通过const引用传递(如果函数不需要本地副本),只有在分析告诉我时才会担心它。
有些人会说你可以不假思索地内联小类方法。这可能会提高运行时性能,但如果有大量内联,它也可能会延长编译时间。如果类方法是库API的一部分,那么最好不要内联它,无论它有多小。这是因为内联函数的实现必须对其他模块/类可见。如果您在内联函数/方法中更改了某些内容,则需要重新编译引用它的其他模块。
当我第一次开始编程时,我会尝试微观优化一切(那是我的电气工程师)。真是浪费时间!
如果您使用的是嵌入式系统,那么事情会发生变化,您无法将记忆视为理所当然。但这是另一整套蠕虫。
答案 7 :(得分:5)
vector
来查看未知大小的数据。如果您要反复拨打push_back()
,请使用reserve()
或使用deque
代替。list
可能是正确的选择。deque
可能是正确的选择。list
可能是错误的选择。list
,但您从未在列表中向后移动,那么您可能会发现forward_list
更符合您的喜好。它不会更快,但会占用更少的空间。请注意,此建议越适用于容器越大。对于较小的容器,vector
可能总是正确的选择,因为较低的常数因素。如有疑问,请以基准为准。
unordered_foo
/ hash_foo
,则没有多少选择。使用四个容器中的任何一个都适合您的需要。unordered_foo
,如果你不关心元素的顺序,你可以使用它而不是有序的版本,并且你有一个很好的哈希函数。dynamic_cast
dynamic_cast
有时是做某事的唯一选择,但通常可以通过改进设计来消除dynamic_cast
的使用。dynamic_cast
替换为typeid
,后跟static_cast
。答案 8 :(得分:5)
我喜欢这个问题,因为它要求一些“好习惯”。 我发现编程中可取的某些东西最初是一件苦差事,但一旦成为习惯就变得可以接受甚至变得容易。
一个例子是始终使用智能指针而不是原始指针来控制堆内存生存期。当然,另一个相关的问题是养成了一直使用RAII进行资源获取和发布的习惯。另一个是始终使用异常进行错误处理。
这三种方法倾向于简化代码,从而使代码更小,更快,更容易理解。
你也可以隐式内联getter和setter;总是充分利用构造函数中的初始化列表;并始终使用std库中提供的find和其他相关函数,而不是制作自己的循环。
不是特别是C ++,但通常值得避免数据复制。在具有大量内存分配的长时间运行的程序中,将内存分配视为设计的主要部分是值得的,因此您使用的内存来自重用的池,尽管这不一定是常见的事情。被认为值得养成习惯。
还有一件事 - 如果您需要功能,请不要将代码从一个地方复制到另一个地方 - 使用一个功能。这样可以减小代码大小,并且可以更轻松地优化使用此功能的所有场所。
答案 9 :(得分:5)
以下是关于此主题的精彩文章:How To Go Slow
答案 10 :(得分:4)
除非您确定其他容器类型更好,否则请使用'std :: vector'。即使'std :: deque,'std :: list','std :: map'等似乎是更加方便的选择,一个向量在内存使用和元素访问\迭代次数上都超过它们。
此外,更喜欢使用容器成员算法(即'map.equal_range(...)')而不是全局对应物('std :: equal_range(begin(),end()...)')< / p>
答案 11 :(得分:4)
模板!使用模板可以减少代码量,因为您可以使用可以使用多种数据类型重用的类或函数/方法。
请考虑以下事项:
#include <string>
using std::basic_string;
template <class T>
void CreateString(basic_string<T> s)
{
//...
}
basic_string可以由char,wchar_t,unsigned char或unsigned wchar_t组成。
模板也可以用于一系列不同的东西,比如traits,class specialization,甚至用于将int值传递给类!
答案 12 :(得分:3)
避免尽可能多次迭代同一数据集。
答案 13 :(得分:2)
This page sum up all you have to know about optimization in C++ (be it while or after writing software).这是非常好的建议并且非常有用 - 并且可以在项目的优化阶段用作有用的提醒。
它有点旧,所以你也必须知道你的编译器已经完成了优化(比如NRVO)。
除此之外,阅读已经被引用的Effective C ++,More Effective C ++,Effective STL和C ++ Coding Standards也很重要,因为它解释了很多关于语言和STL中发生的事情,允许通过更好地了解正在发生的事情,您可以更好地优化您的具体案例。
答案 14 :(得分:2)
我建议你阅读Jon Bentley的"Programming Pearls"第二章(“表演”)。它不是特定于C ++的,但这些技术也可以在C或C ++中应用。该网站仅包含本书的部分内容,我建议您阅读本书。
答案 15 :(得分:2)
我习惯于更喜欢写++i
而不是i++
,而不是当i
是int
时它会带来任何性能提升但i
时情况有所不同是一个iterator
,可能有一个复杂的实现。
然后假设您来自C编程语言,失去了在函数开头声明所有变量的习惯:在函数流中需要时声明变量,因为函数可能包含早期{{1}在一些在开头初始化的变量被有效地使用之前的语句。
除此之外,另一个资源是Herb Sutter(他再次)和Alexei Alexandrescu的C++ Coding Standards: 101 Rules, Guidelines, and Best Practices。
还有最新版本的Scott Meyers的Effective C ++:Effective C++: 55 specific ways to improve your programs and designs。
最后,我想提一下托尼·阿尔布雷希特的Pitfalls of Object Oriented Programming演讲:并不是说它包含了你可以盲目追随的经验法则,但这是一个非常有趣的读物。
答案 16 :(得分:2)
这是我过去提到过的一个列表 - http://www.devx.com/cplus/Article/16328/0/page/1。除此之外,谷歌搜索c ++性能提示产生了很多。
答案 17 :(得分:1)
你可以通过这种方式获得的性能提升是令人惊讶的。对于我来说,计算量很大的应用程序1500次。不是粗暴的,而是在主要软件包中编写的类似数据结构。
我不会因为事后的影响而感到困扰。这只能节省某些(不重要)的情况,并且提到的大部分内容都是类似的东西,可能偶尔会在这里和那里刮掉额外的1%,但通常不值得打扰。
答案 18 :(得分:1)
这个将具有非常好的通用c ++优化技术:
http://www.tantalon.com/pete/cppopt/main.htm
使用分析器查看应用程序的哪个部分运行缓慢,然后使用优化技术。
我使用valgrind和callgrind工具进行分析,它会给你哪些行花费多少。
valgrind --tool = callgrind
答案 19 :(得分:1)
这里有很多好的建议。
养成良好习惯的最佳方法之一就是强迫自己。为此,我喜欢PC-Lint。 PC-Lint实际上将强制执行Scott Meyer的Effective C ++&amp;更有效的C ++规则。遵守Lint规则往往会使维护更容易,更容易出错,并且代码更清晰。当你意识到lint通常会产生比源代码更多的输出时,不要太疯狂;我曾经参与过一个包含150MB源代码和1.8GB Lint消息的项目。
答案 20 :(得分:0)
为什么到目前为止没人提到它?为什么每个人都变得很穷++i
?
您可以轻松完成的最好的小事之一,不要认真您的代码:
有效的C ++ 作者:Scott Meyers ,第20项:
首选将参考传递给const传递值
示例:
// this is a better option
void some_function(const std::string &str);
// than this:
void some_function(std::string str);
如果短std::string
你可能不会赢得太多,但传递大这样的对象,可以为你节省大量的计算能力,因为你可以避免冗余复制。如果您忘记实施复制构造函数,也可以将您从一两个错误中解救出来。
答案 21 :(得分:0)
答案 22 :(得分:0)
提高这些技能的最佳方法是阅读书籍和文章,但我可以帮助您提供一些提示:
答案 23 :(得分:0)
喜欢使用预增量。
使用int / pointer等没有区别 但是对于类类型,标准的实现方式需要创建一个新对象。
所以更喜欢预增量。以防万一在以后的情况下,类型会发生变化 然后,您无需修改代码即可应对。