Profiling
This page compiles some profiling conclusions and tips:
Contents
Method GetSize()
- Avoid:
for (int i=0;i<v.GetSize();++i) {}
- Prefer:
const unsigned int size = v.GetSize(); for (unsigned int i=0;i<size;++i) {}
Defensive Programming
- Avoid defensive programming in function called many times. i.e. avoid testing for programming errors. In particular never use std::vector<>::at()
- Prefer narrow contracts, and test for possible runtime errors before calling functions that have contracts. For more information, see these posts (in French).
Pixel values
Pixel values from otb::VectorImage
are actually dynamic vectors. As such, avoid to create, copy and destroy pixel values. Instead prefer to minimize the number of pixel objects created (e.g. try to stick to only one instance declared outside a loop), and then assign them new values.
This is also true for generic algorithms that may work on scalar as well as vector pixels.
Calling static_cast<>
on pixels is not recommended either as this may induce a converting-construction. Prefer the new itk::CastInto<>
or itk::MoveInto<>
functions which will be proposed as a patch.
A more thorough analysis will be conducted here.
Copy and Assignments
When trying to decide how a copy-constructor and how copy-assignment shall be handled, the first step is to decide whether the class really needs to support duplication. Most of the time, the answer is NO. That's why many ITK and OTB classes have their copy-constructor and copy-assignment operator declared private and not implemented.
It's also possible to inherit from classes like boost::noncopyable, or to use C++11 "= delete" annotation.
However, sometimes we have to define new classes that have value semantics. These classes need to support copy-construction and copy-assignment. Almost two decades ago, it was trendy to check for self-assignment -- and to leave the code in a non-exception safe state. Since then, we have learned how to have our code robust to exceptions. It happens such codes do also resist to self-assignment. This means that in the end, a correct code will resist to self-assignment, and the self-assignment test will optimize the case where we write (directly or indirectly) a = a;, and slightly degrade all other cases.
As the a = a; case is more than unlikely to appear in usual programs, instead of optimizing it at the expense of all other cases, let's instead NEVER test for self assignment.
x^y
To compute the square of a number, prefer the direct use of the multiplication operator instead of pow() variants (std::pow() or vcl_pow).
In the same idea, when x is the unsigned integral 2, and y a positive integer, you can directly use the bitshift operator <<. For instance, instead of pow(2, y), write 1 << y.
Variable declaration
Avoid to declare variables in two pass: first the declaration (which may incur a default or a zero construction), then the initialisation. Always prefer to do everything in one pass.
It's not actually a performance issue, with most types, but a QA issue. See on this subject this FAQ Q/A (in French), or the C++CoreGuideline ES.22 & ES.21. (Yes setters have the same quality issue)
Dynamic allocation with sizes known at compile time
Avoid the use of dynamic array types with (small) sizes that are known at compile time.
For instance, Don't use vnl_vector when the size is a constant like 3 (or other magic numbers), or named constants like ImageDimension.
Prefer instead static array types like boost::array, vnl_vector_fixed, ...