Dhia Hassen
Dhia Hassen

Reputation: 526

Virtual method VS std::function member variable in terms of performance

A game engine has this class :

class  MouseListener{
public :
MouseListener();
virtual void OnMouseDown(int mx,int my);
virtual void OnMouseUp(int mx,int my);
.
.
.
};

Each object that wants to listen to mouse input , have to inherent that class and override it's methods . To not have to declare a new type each time , the class is modified to :

class  MouseListener{
public :
MouseListener();
std::function <void(MouseListener*,int,int)>OnMouseDown;
std::function <void(MouseListener*,int,int)>OnMouseUp;
.
.
.
};

now using the class can be done this way :

MouseListener * m = new MouseListener();
m->OnMouseDown = [](MouseListener * thiz,int x,int y){
   //// do something
};

From the input system , only the functions of MouseListener that are not null ( assigned ) are called ( with thiz = the mouse listener ). knowing that the class is used from an external library ( static link ) , which is better in terms of performance ?

NOTE : None of those function will be called unless a mouse event is received , when that happens , the appropriate function is called for each object listening to mouse input ( not supposed to be lot , <50)

Upvotes: 14

Views: 5531

Answers (1)

David Haim
David Haim

Reputation: 26496

It really depends on the usage of the virtual function vs. the function object.

although std::function may be slower than a virtual call*, std::function has short buffer optimization , which may prevent dynamic memory allocation (which you would probably have in the virtual version).
this itself may outperform anything you might do with regular polymorphism.

Internally (but not guaranteed), the std::function object anyway uses a virtual call for the type erasure, so I'd say the difference is negligible.

A tip - make sure to check if the std::function is valid (by calling if(static_cast<bool>(myFunction))). the compiler will insert a check to see if the function is empty. if not, the program will throw std::bad_function_call. the developer check will make the compiler remove his check and the code related to std::bad_function_call when optimizations are turned on, leaving much "smoother" assembly code.

when dealing with performance and C++ in my experience, it's much more important to optimize away memory allocations, inter-thread-contention and bad data structures which work bad with the cache. usually, it's much more worthy optimizations than optimizing CPU-stuff away (like virtual function vs. std::function)

*A decent compiler can implement the type erasure as a virtual function + the given lambda as inlined function. theoretically, it should not be slower than a regular virtual function. on the other hand, if the function object gets a non-inlinable callable, like a function pointer, it may use two indirection in order to launch the function. this may be slower than a regular virtual call. it depends.

Upvotes: 12

Related Questions