Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overhead with std::function

I have seen many instances where people have advised against using std::function<> because it is a heavyweight mechanism. Could someone please explain why that is so?

like image 919
Curious Avatar asked Dec 09 '22 00:12

Curious


1 Answers

std::function is a type erasure class.

It takes whatever it is constructed from, and erases everything except:

  • Invoke with the signature in question (with possible implicit casting)
  • Destroy
  • Copy
  • Cast back to exact original type

and possibly

  • Move

This involves some overhead. A typical decent-quality std::function will have small object optimization (like small string optimization), avoiding a heap allocation when the amount of memory used is small.

A function pointer will fit in there.

However, there is still overhead. If you initialize a std::function with a compatible function pointer, instead of directly calling the function pointer in question, you do a virtual function table lookup, or invoke some other function, which then invokes the function pointer.

With a vtable implementation, that is a possible cache miss, an instruction cache miss, then another instruction cache miss. With a function pointer, the pointer is probably stored locally, and it is called directly, resulting on one possible instruction cache miss.

On top of this, in practice compilers understand function pointers better than std::functions: a number of compilers can figure out that the pointer is constant value during inlining or whole program optimization. I have never seen one that pulls that off with std::function.

For larger objects (say larger than sizeof(std::string) in one implementation), a heap allocation is also done by the std::function. This is another cost. For function pointers and reference wrappers, SOO is guaranteed by the standard.


Directly storing the lambda without storing it in a std::function is even better than a function pointer: in that case, the code being run is implicit in the type of the lambda. This makes it trivial for code to work out what is going to happen when it is called, and inlining easy for the compiler.

Only do type erasure when you need to.

like image 170
Yakk - Adam Nevraumont Avatar answered Dec 21 '22 23:12

Yakk - Adam Nevraumont