一般 C++ 性能改进技巧
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2030189/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
General C++ Performance Improvement Tips
提问by genesys
Could someone point me to an article, or write some tips right here about some C++ programming habits that are generally valid (no real drawbacks) and improves performance? I do not mean programming patterns and algorithm complexity - I need small things like how you define your functions, things to do/to avoid in loops, what to allocate on the stack, what on the heap, and so on.
有人可以给我指点一篇文章,或者在这里写一些关于一些 C++ 编程习惯的提示,这些习惯通常有效(没有真正的缺点)并提高性能?我的意思不是编程模式和算法复杂性——我需要一些小东西,比如你如何定义你的函数,在循环中要做/避免的事情,在堆栈上分配什么,在堆上分配什么,等等。
It's not about making a particular software faster, also it's not about how to create a clean software design, but rather programming habits that - if you always apply them, you will make your code rather a little bit faster than a little bit slower.
这不是关于让特定软件更快,也不是关于如何创建干净的软件设计,而是关于编程习惯——如果你总是应用它们,你会让你的代码更快而不是慢一点。
回答by Laurence Gonsalves
A number of the tips in Effective C++, More Effective C++, Effective STLand C++ Coding Standardsare along this line.
Effective C++, More Effective C++, Effective STL和C++ Coding Standards中的许多技巧都在这条线上。
A simple example of such a tip: use preincrement (++i) rather than postincrement (i++) when possible. This is especially important with iterators, as postincrement involves copying the iterator. You optimizer maybe able to undo this, but it isn't any extra work to write preincrement instead, so why take the risk?
此类技巧的一个简单示例:尽可能使用前增量 (++i) 而不是后增量 (i++)。这对于迭代器尤其重要,因为后增量涉及复制迭代器。您的优化器可能能够撤消此操作,但是编写预增量并不是任何额外的工作,那么为什么要冒险呢?
回答by Michael Kristofik
If I understand you correctly, you're asking about avoiding Premature Pessimization, a good complement to avoiding Premature Optimization. The #1 thing to avoid, based on my experience, is to not copy large objects whenever possible. This includes:
如果我理解正确,您是在询问避免过早悲观化,这是避免过早优化的一个很好的补充。根据我的经验,要避免的第一件事是尽可能不要复制大对象。这包括:
- pass objects by (const) reference to functions
- return objects by (const) reference whenever practical
- make sure you declare a reference variable when you need it
- 通过(const)对函数的引用传递对象
- 在可行时通过(const)引用返回对象
- 确保在需要时声明引用变量
This last bullet requires some explanation. I can't tell you how many times I've seen this:
最后一个要点需要一些解释。我无法告诉你我见过多少次:
class Foo
{
const BigObject & bar();
};
// ... somewhere in code ...
BigObject obj = foo.bar(); // OOPS! This creates a copy!
The right way is:
正确的方法是:
const BigOject &obj = foo.bar(); // does not create a copy
These guidelines apply to anything larger than a smart pointer or a built-in type. Also, I highly recommend investing time learning to profile your code. A good profiling tool will help catch wasteful operations.
这些指南适用于任何大于智能指针或内置类型的内容。此外,我强烈建议您花时间学习分析您的代码。一个好的分析工具将有助于捕获浪费的操作。
回答by Michael Kristofik
A few of my pet peeves:
我的一些宠物烦恼:
- Don't declare (actually, define) object variables before their use/initialization (as in C). This necessitates that the constructor AND assigment operator functions will run, and for complex objects, could be costly.
- Prefer pre-increment to post-increment. This will only matter for iterators and user-defined types with overloaded operators.
- Use the smallest primitive types possible. Don't use a long int to store a value in the range 0..5. This will reduce overall memory usage, improving locality and thus overall performance.
- Use heap memory (dynamic allocation) only when necessary. Many C++ programmers use the heap by default. Dynamic allocations and deallocations are expensive.
- Minimize the use of temporaries (particularly with string-based processing). Stroustrup presents a good technique for defining the logical equivalent of ternary and higher-order arithmetic operators in "The C++ Programming Language."
- Know your compiler/linker options. Also know which ones cause non-standard behavior. These dramatically affect runtime performance.
- Know the performance/functionality tradeoffs of the STL containers (e.g., don't frequently insert into a vector, use a list).
- Don't initialize variables, objects, containers, etc., when they are going to be unconditionally assigned to.
- Consider the order of evaluation of compound conditionals. E.g., given
if (a && b)
, if b is more likely to be false, place it first to save the evaluation of a.
- 不要在使用/初始化之前声明(实际上是定义)对象变量(如在 C 中)。这需要运行构造函数和赋值运算符函数,对于复杂的对象,可能代价高昂。
- 更喜欢前增量而不是后增量。这仅适用于迭代器和具有重载运算符的用户定义类型。
- 尽可能使用最小的原始类型。不要使用 long int 来存储 0..5 范围内的值。这将减少整体内存使用,提高局部性,从而提高整体性能。
- 仅在必要时使用堆内存(动态分配)。许多 C++ 程序员默认使用堆。动态分配和解除分配是昂贵的。
- 尽量减少临时文件的使用(尤其是基于字符串的处理)。Stroustrup 在“C++ 编程语言”中提出了一种很好的技术来定义三元和高阶算术运算符的逻辑等价物。
- 了解您的编译器/链接器选项。还知道哪些会导致非标准行为。这些会极大地影响运行时性能。
- 了解 STL 容器的性能/功能权衡(例如,不要频繁插入向量,使用列表)。
- 不要初始化变量、对象、容器等,当它们将被无条件分配时。
- 考虑复合条件的求值顺序。例如,给定
if (a && b)
,如果 b 更可能为假,则将其放在首位以保存对 a 的评估。
There are many other "bad habits" which I won't mention, because in practice, modern compilers/optimizers will eliminate the ill effects (for example, return value optimization vs. pass-by-reference, loop unwinding, etc.).
还有许多其他“坏习惯”我不会提及,因为在实践中,现代编译器/优化器将消除不良影响(例如,返回值优化与传递引用、循环展开等)。
回答by Nikolai Fetissov
One of the good starting points is the Sutter's Guru of the weekseries, and the Exceptional C++books that grew out of that.
良好的起点之一是Sutter 的本周大师系列,以及由此产生的Exceptional C++书籍。
回答by Tommy Andersen
The "Optimizing Software in C++" by Agner Fogis generally one of the best references for optimization techniques both simple, but definitely also more advanced. One other great advantage is that it is free to read on his website. (See link in his name for his website, and link on paper title for pdf).
在“优化软件在C ++”,由昂纳雾一般是优化技术既简单最好的参考之一,但肯定也更先进。另一个很大的优势是可以在他的网站上免费阅读。(参见他的网站链接,以及 pdf 论文标题链接)。
Edit:Also remember that 90% (or more) of the time is spent in 10% (or less) of the code. So in general optimizing code is really about pinpointing your bottlenecks. Further more it is important and useful to know that modern compilers will do optimzation much better than most coders, especially micro optimizations such as delaying initialization of variables, etc. The compilers are often extremely good at optimizing, so spend your time writing stable, reliable and simple code.
编辑:还要记住,90%(或更多)的时间花在 10%(或更少)的代码上。所以总的来说,优化代码实际上是要查明瓶颈。更重要的是,现代编译器会比大多数编码器更好地进行优化,尤其是诸如延迟变量初始化等微优化。编译器通常非常擅长优化,因此请花时间编写稳定、可靠和简单的代码。
I'd argue that it pays to focus more on the choice of algorithm than on micro optimizations, at least for the most part.
我认为至少在大多数情况下,更多地关注算法的选择而不是微观优化是值得的。
回答by Fred
Use functors (classes with operator()
implemented) instead of function pointers. The compiler has an easier job inlining the former. That's why C++'s std::sort
tends to perform better (when given a functor) than C's qsort
.
使用函子(已operator()
实现的类)而不是函数指针。编译器可以更轻松地内联前者。这就是为什么 C++std::sort
往往比 C 表现得更好(当给定函子时)qsort
。
回答by Emile Cormier
It seems from your question that you already know about the "premature optimization is evil" philosophy, so I won't preach about that. :)
从你的问题来看,你似乎已经知道“过早优化是邪恶的”哲学,所以我不会讲这个。:)
Modern compilers are already pretty smart at micro-optimizing things for you. If you try too hard, you can often make things slower than the original straight-forward code.
现代编译器在为您微优化事物方面已经非常聪明。如果你尝试得太用力,你通常会使事情比原来的直接代码慢。
For small "optimizations" you can do safely without thinking, and which doesn't affect much the readability/maintability of the code, check out the "Premature Pessimization" section of the book C++ Coding Standardsby Sutter & Alexandrescu.
对于小的“优化”,您可以不假思索地安全进行,并且不会对代码的可读性/可维护性产生太大影响,请查看Sutter 和 Alexandrescu所著《C++ 编码标准》一书的“过早悲观化”部分。
For more optimization techniques, check out Efficient C++by Bulka & Mayhew. Only use when justified by profiling!
有关更多优化技术,请查看Bulka 和 Mayhew 的Efficient C++。仅在通过分析证明合理时使用!
For good general C++ programming practices, check out:
有关良好的通用 C++ 编程实践,请查看:
- C++ Coding Standardsby Sutter & Alexandrescu (must have, IMHO)
- Effective C++/STL seriesby Scott Meyers
- Exceptional C++series by Herb Sutter
- Sutter 和 Alexandrescu 的C++ 编码标准(必须有,恕我直言)
- Scott Meyers 的Effective C++/STL 系列
- Herb Sutter出色的 C++系列
Off the top of my head, one good general performance practice is to pass heavyweight objects by reference, instead of by copy. For example:
在我的脑海中,一种良好的通用性能实践是通过引用传递重量级对象,而不是通过复制。例如:
// 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;
}
For a small object like a complex number or 2-dimensional (x, y) point, the function will likely run faster with the object passed by copy.
对于像复数或二维 (x, y) 点这样的小对象,通过复制传递的对象,函数可能会运行得更快。
When it comes to fixed-size, medium-weight objects, it's not so clear if the function will run faster with a copy or a reference to the object. Only profiling will tell. I usually just pass by const reference (if the function doesn't need a local copy) and only worry about it if profiling tells me to.
当涉及到固定大小、中等重量的对象时,函数是否会通过对象的副本或引用运行得更快还不是很清楚。只有分析会告诉你。我通常只通过 const 引用(如果函数不需要本地副本)并且只在分析告诉我时担心它。
Some will say that you can inline small class methods without thinking. This may give you a runtime performance boost, but it may also lengthen your compile time if there is a heavy amount of inlining. If a class method is part of a library API, it might be better not to inline it, no matter how small it is. This is because the implementation of inline functions has to be visible to other modules/classes. If you change something in that inline function/method, then other modules that reference it need to be re-compiled.
有人会说你可以不假思索地内联小类方法。这可能会提高运行时性能,但如果有大量内联,也可能会延长编译时间。如果类方法是库 API 的一部分,最好不要内联它,无论它有多小。这是因为内联函数的实现必须对其他模块/类可见。如果您更改了该内联函数/方法中的某些内容,则需要重新编译引用它的其他模块。
When I first started to program, I would try to micro-optimize everything (that was the electrical engineer in me). What a waste of time!
当我第一次开始编程时,我会尝试对一切进行微观优化(这就是我的电气工程师)。真是浪费时间!
If you're into embedded systems, then things change and you can't take memory for granted. But that's another whole can of worms.
如果您使用嵌入式系统,那么事情就会发生变化,您不能认为内存是理所当然的。但那又是一整罐蠕虫。
回答by coppro
Use the right container
使用正确的容器
Sequence containers
序列容器
- Do notuse
vector
for data of unknown size if you are going to keep adding data to it. If you are going to repeatedly callpush_back()
, either usereserve()
or use adeque
instead. - If you are going to be adding/removing data in the middle of the container,
list
is probably the right choice. - If you are going to be adding/removing data from both ends of the container,
deque
is probably the right choice. - If you need to access the nth element of the container,
list
is probably the wrong choice. - If you need to both access the nth element of the container and add/remove elements in the middle, benchmark all three containers.
- If you have C++0x capability and are using a
list
but you never move backwards through the list, you may findforward_list
more to your liking. It won't be faster but it will take up less space.
- 千万不能使用
vector
,如果你要继续添加数据为未知大小的数据。如果您要重复调用push_back()
,请使用reserve()
或使用 adeque
代替。 - 如果您要在容器中间添加/删除数据,这
list
可能是正确的选择。 - 如果您要从容器的两端添加/删除数据,这
deque
可能是正确的选择。 - 如果需要访问容器的第 n 个元素,
list
可能是错误的选择。 - 如果您需要访问容器的第 n 个元素并在中间添加/删除元素,请对所有三个容器进行基准测试。
- 如果您具有 C++0x 功能并且正在使用 a
list
但您从不向后移动列表,您可能会发现forward_list
更多您喜欢的东西。它不会更快,但会占用更少的空间。
Note that this advice becomes more applicable the larger the container. For smaller containers, vector
may always be the right choice simply because of the lower constant factors. When in doubt, benchmark.
请注意,容器越大,此建议就越适用。对于较小的容器,vector
仅仅因为较低的常数因素,它可能总是正确的选择。如有疑问,请进行基准测试。
Associative containers
关联容器
- If you do not have TR1, C++0x, or a vendor-specific
unordered_foo
/hash_foo
, there isn't a ton of choice. Use whichever of the four containers is appropriate to your needs. - If you do have an
unordered_foo
, use it instead of the ordered version if you do not care about the order of the elements and you have a good hash function for the type.
- 如果您没有 TR1、C++0x 或特定于供应商的
unordered_foo
/hash_foo
,则没有很多选择。使用适合您需要的四个容器中的任何一个。 - 如果您有一个
unordered_foo
,如果您不关心元素的顺序并且您有一个很好的类型散列函数,请使用它而不是有序版本。
Use exceptions judiciously
明智地使用异常
- Don't use exceptions in your normal code path. Save them for when you actually have an exceptional circumstance.
- 不要在正常的代码路径中使用异常。将它们保存到您确实遇到特殊情况时。
Love templates
爱情模板
- Templates will cost you at compile time and space-wise, but the performance gains can be amazing if you have calculations that would otherwise be performed at run-time; sometimes even something so subtle as a downcast.
- 模板会在编译时和空间方面花费你,但如果你的计算本来是在运行时执行的,那么性能提升会是惊人的;有时甚至是像沮丧这样微妙的事情。
Avoid dynamic_cast
避免 dynamic_cast
dynamic_cast
is sometimes the only choice for doing something, but oftentimes the use ofdynamic_cast
can be eliminated by improving design.- Don't replace
dynamic_cast
with atypeid
followed by astatic_cast
either.
dynamic_cast
有时是做某事的唯一选择,但通常dynamic_cast
可以通过改进设计来消除使用。- 不要更换
dynamic_cast
一个typeid
接着一个static_cast
无论是。
回答by Nemanja Trifunovic
Here is a nice article on the topic: How To Go Slow
这是一篇关于这个主题的好文章:如何慢慢来
回答by Permaquid
I like this question because it is asking for some "good habits". I have found that certain things that are desirable in programming are initially a chore, but become acceptable and even easy once they become habits.
我喜欢这个问题,因为它要求一些“好习惯”。我发现某些在编程中令人向往的事情最初是一件苦差事,但一旦成为习惯,就会变得可以接受甚至变得容易。
One example is always using smart pointers instead of raw pointers to control heap memory lifetime. Another, related of course, is developing the habit of always using RAII for resource acquisition and release. Another is always using exceptions for error handling.
These three tend to simplify code, thereby making it smaller and so faster, as well as easier to understand.
一个例子是总是使用智能指针而不是原始指针来控制堆内存寿命。另一个相关的当然是养成始终使用 RAII 进行资源获取和释放的习惯。另一个总是使用异常进行错误处理。这三个倾向于简化代码,从而使其更小、更快,并且更易于理解。
You could also make getters and setters implicitly inline; always make full use of initializer lists in constructors; and always use the find and other related functions that are provided in the std library, instead of crafting your own loops.
你也可以使 getter 和 setter 隐式地内联;始终在构造函数中充分利用初始化列表;并始终使用 std 库中提供的 find 和其他相关函数,而不是制作自己的循环。
Not specifically C++, but it is often worthwhile to avoid data copying. In long-running programs with a lot of memory allocation it can be worthwhile to consider memory allocation as a major part of the design, so that the memory you use comes from pools that are reused, although this is not necessarily a common enough thing to be considered worthy of forming a habit.
不是专门的 C++,但避免数据复制通常是值得的。在具有大量内存分配的长时间运行的程序中,值得将内存分配视为设计的主要部分,以便您使用的内存来自重用的池,尽管这不一定是足够常见的事情被认为值得养成习惯。
One more thing - do not copy code from one place to another if you need the functionality - use a function. This keeps code size small and makes it easier to optimize all the places that use this functionality.
还有一件事 - 如果需要功能,不要将代码从一个地方复制到另一个地方 - 使用函数。这可以保持较小的代码大小,并且可以更轻松地优化所有使用此功能的地方。