Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid memory allocation with std::function and member function

Tags:

c++

gcc

This code is just for illustrating the question.

#include <functional> struct MyCallBack {     void Fire() {     } };  int main() {     MyCallBack cb;     std::function<void(void)> func = std::bind(&MyCallBack::Fire, &cb); } 

Experiments with valgrind shows that the line assigning to func dynamically allocates about 24 bytes with gcc 7.1.1 on linux.

In the real code, I have a few handfuls of different structs all with a void(void) member function that gets stored in ~10 million std::function<void(void)>.

Is there any way I can avoid memory being dynamically allocated when doing std::function<void(void)> func = std::bind(&MyCallBack::Fire, &cb); ? (Or otherwise assigning these member function to a std::function)

like image 957
binary01 Avatar asked Sep 11 '17 20:09

binary01


People also ask

Does std :: function allocate memory?

std::function can store objects of arbitrary size, this means it must perform dynamic memory allocation in some cases. there are certain types for which std::function is guaranteed not to throw exceptions. This implies that there are certain types it must store without dynamic memory allocation.

Does STD function always allocate?

Many std::function implementations will avoid allocations and use space inside the function class itself rather than allocating if the callback it wraps is "small enough" and has trivial copying. However, the standard does not require this, only suggests it.

Why is STD function so slow?

If it is small, like 3-5 CPU instructions then yes std::function will make it slower, because std::function is not inlined into outer calling code. You should use only lambda and pass lambda as template parameter to other functions, lambdas are inlined into calling code.

Does std :: variant allocate memory?

According to cppreference ::std::variant must not allocate dynamic memory.


1 Answers

Unfortunately, allocators for std::function has been dropped in C++17.

Now the accepted solution to avoid dynamic allocations inside std::function is to use lambdas instead of std::bind. That does work, at least in GCC - it has enough static space to store the lambda in your case, but not enough space to store the binder object.

std::function<void()> func = [&cb]{ cb.Fire(); };     // sizeof lambda is sizeof(MyCallBack*), which is small enough 

As a general rule, with most implementations, and with a lambda which captures only a single pointer (or a reference), you will avoid dynamic allocations inside std::function with this technique (it is also generally better approach as other answer suggests).

Keep in mind, for that to work you need guarantee that this lambda will outlive the std::function. Obviously, it is not always possible, and sometime you have to capture state by (large) copy. If that happens, there is no way currently to eliminate dynamic allocations in functions, other than tinker with STL yourself (obviously, not recommended in general case, but could be done in some specific cases).

like image 148
SergeyA Avatar answered Oct 08 '22 19:10

SergeyA