Version 2 (modified by simon, 20 years ago) (diff) |
---|
C++ Performance Tweaking Tips
Inline Functions
Function declared as inline will be included in the calling code in compilation time. This speeds up execution because all the branching stuff doesn't have to be executed. The speedup is maxed out, when these functions are only very small, so when the execution of the function needs approximatly the same time as the branching time. Here a little example:
class Test { public: int getSize(); private: int size; }; inline int Test:getSize() { return this->size; }
This function will be executed aproximatly 10 times faster on a Pentium II based processor. BUT Don't write everywhere inline functions: use it only for time critical stuff, so said functions, that are executed very often during the game-time of orxonox like:
- void !WorldEntity::tick(float time) {} this function is called everytime a frame is rendered
- bool !BaseObject::isFinalized() this function is called from the garbage collector every time he does its job
Don't use it for functions that are normally called before and after the game time or functions that are rarely called at all. Inlining brings some problems, too. First: Inlined code doesn't have to be made inline by the compiler. Some reasons, why this could happen are: loops in the inlined code, recursive code in the inlined code and function calls in the inlined code.
Memory Allocation and Deletion: new, delete
Creation of new objects needs very much time compared to mathematical operations. It can take from 20 to 200 times the time a normal function call costs depending of how deeply the class has been derived (measured on a pentium II computer). Try to make as few new objects you can and recycle them if possible (altough it can lead to strange code).[br] Given the case, that you have to create a new object, try to make it like this:
void ExampleClass::goodStyle() { Object* obj = new Object(); obj->doSomeStuf(); delete obj; }
To free the memory is very important, if the function is called multiple times! [br] If you write it the following way, you don't have to delete it:
void ExampleClass::goodStyle2() { Object obj; obj.doSomeMoreStuf(); }
The difference is, that this obj will be stored as a temporary variable and deleted after the function returns! This leads to some other problems: If you want to give a reference to said object via the argument of a function to another object, never use these temporary variables.
void SomeClass::wantsObjectReference(Object* reference) { /* do something with the reference */ } void ExampleClass::badBadBad() { Object obj; /* this is only a local reference automatically deleted after function return */ SomeClass* sc = new SomeClass(); /* creation of a new object needs much time, avoid it if possible - here we need it */ sc->wantObjectReference(&obj); /* BAD BAD BAD BAD!!!!! */ delete sc; }
The compile will complain about such things with a message like this: "WARNING: taking address of a temporary". And Mr. compiler is absolutly right! A better way would be:
void SomeClass::wantsObjectReference(Object* reference) { /* do something with the reference */ } void ExampleClass::badBadBad() { Object* obj = new Object*(); /* this is only a local reference! automatically deleted after function return */ SomeClass* sc = new SomeClass(); /* creation of a new object needs much time, avoid it if possible - here we need it */ sc->wantObjectReference(obj); /* BAD BAD BAD BAD!!!!! */ delete sc; }
Redundant code
As anything which can be done fast should be done fast, we can optimise many functions which take and return values. Often code for an operator or something similar will look like that:
CVector3f operator+( CVector3f v ) { CVector3f returnVector; returnVector.m_x = m_x + v.m_x; returnVector.m_y = m_y + v.m_y; returnVector.m_z = m_z + v.m_z; return returnVector; }
Now what's wrong here is the local variable. When the temporary object is made on the first line, the constructor is called and the object initialized. But we didn't want that to happen! We assign new values anyways in the next lines. Always keep in mind the time wasted when creating objects. [br] The copy constructor is called again in the end of the function, as returnVector is a local variable. This just shows why this style is very bad. [br] A problem which lies hidden is the problem of parameter. As the parameter is a copy of the original argument, we get another useless object construction. [br] A better way would be:
CVector3f operator+( const CVector3f &v ) const { return CVector3f( m_x + v.m_x, m_y + v.m_y, m_z + v.m_z ) }
This implementation saves us time as 2 less copy constructors are called. In this particular problem we could save time by knowing waht happens behind the scenes.