Writing Efficient C++ Code
3 points by jeaye
3 points by jeaye
I've mixed feelings about this article. While some high-level aspects are nicely written up, some details are not.
Sometimes, however, there is no single function, or small group of functions, critical to performance. Different parts of the program each take a small percentage of the time, while the program as a whole is slow because it is generally poorly written. We are no better off if we discover that optimizing a time-consuming function would require redesigning the data structures and rewriting a large part of the program. That is why it is worth caring about the performance of our code in our everyday programming work.
I particularly like this bit at the end -- badly chosen data structures obfuscate performance cost and make optimization not just difficult, but also make it hard to estimate how much benefit an optimization can provide.
However, I disagree on some details:
I'm missing discouragement of virtual functions (optimization barrier, dispatch overhead), exceptions, standard library containers (e.g., std::shared_ptr (atomic ref counts), practically all containers, std::function, ...), and hash maps (arrays are often preferable; esp. pointer-to-x maps can be avoided in many cases: the CPU already has a fast built-in mechanism to map a pointer to something). And I'm missing the most important advice: profile first and measure afterwards -- there's no point in optimizing cold parts of programs and optimizations should have a measurable benefit:
If you make an optimization and don't measure to confirm the performance increase, all you know for certain is that you've made your code harder to read. (https://martinfowler.com/ieeeSoftware/yetOptimization.pdf)
I think the discouragement of exceptions was assumed, if you're disabling RTTI and exception handling. At least I'd hope so! Unfortunately, I think this article is very surface level, and 13 years out of date. I'd love a more recent one that talks about constexpr (and conditional constexpr), consteval, lifetime management (std::construct_at and so on).
I don't quite agree with profile first and measure afterwards, I think you have to start by writing in a way that's not pessimized. That's not the same thing as optimizing right away, but the choice of data structures and algorithms has a large impact on the way the code can be improved later on, since you can lock yourself into a very ugly code path.
I think this should have had (2013) added to the title if I understand the rules correctly.
DOD and SOA/AOS are applicable to more languages than C++, and that was true even in 2013. Still, it's good to know these to help write machine sympathetic code.
In many cases it is therefore worth forgoing and completely disabling some features, such as exception handling or RTTI.
"Many" is doing some really heavy lifting here. Measure, compare, and justify why you're disabling them. I'm not a fan of exception handling or RTTI but you shouldn't just blindly do this, especially if you're using third party libraries that can throw exceptions. I'd love to discuss why I hate the design of exceptions in C++ separately, but that's a whole other can of worms. I don't think the audience of this article is fit to be writing RTTI and error handling themselves. The story of memory allocation in C++ has really improved since this article came out, PMR is sufficient. If it's not, I assume you have a very niche situation and you are already aware of exactly what you need to do.
We know that the C++ keyword
volatileis used to prevent a value from being optimized in a way that would allow the compiler to keep it in a processor register rather than fetching it from its original memory location each time.
This should be taken with a grain of salt. volatile is one of the most abused keywords I see in codebases, giving people a false sense of it being capable of implementing lock-free algorithms.