small object optimization useless in using std::function
Asked Answered
B

1

6

Many topics told us that use small object like lambda expression could avoid heap allocation when using std::function. But my study shows not that way.

This is my experiment code, very simple

#include <iostream>
#include <functional>

using namespace std;

typedef std::function<int(int, int)> FUNC_PROTO;

class Test
{
public:
    int Add(int x, int y) { return x + y; }
};

int main()
{
    Test test;
    FUNC_PROTO functor = [&test](int a, int b) {return test.Add(a, b); };
    cout << functor(1, 2) << endl;
}

And I compile it on centos7, with gcc version 4.8.5 20150623 But the valgrind shows this:

==22903== HEAP SUMMARY:
==22903==     in use at exit: 0 bytes in 0 blocks
==22903==   total heap usage: 1 allocs, 1 frees, 8 bytes allocated

Even if I remove the reference capture, just a plain lambda function. It still cost 1 byte heap allocation.

Is there somthing wrong of me to get small object optimization.

Update:

Thanks for the repies. I think I should add more detail of my experiment.

In order to eliminate the possible cause of refrence capturing. I removed capture, code like this:

FUNC_PROTO functor = [](int a, int b) {return a + b; };

Valgrind shows this:

==16691==   total heap usage: 1 allocs, 1 frees, 1 bytes allocated

Still 1 byte heap allocation.

I also tried this to eliminate possible influence of lambda itself(which I think not)

FUNC_PROTO functor = [](int a, int b) {return a + b; };
FUNC_PROTO functor2 = [](int a, int b) {return a * b; };

FUNC_PROTO test = nullptr;
for ( int i = 0; i < 10; ++i)
{
    if (i % 2 == 0)
    {
        test = functor;
    }
    else
    {
        test = functor2;
    }
}

Valgrind shows:

==17414==   total heap usage: 12 allocs, 12 frees, 12 bytes allocated

That could prove that the functors are not fully stack based object.

And this is my build script:

g++ test.cpp -o test  -std=c++11 -g -O3 -DNDEBUG

This is my valgrind script:

valgrind --log-file=valgrind.log --tool=memcheck --leak-check=full --show-leak-kinds=all ./test
Brita answered 5/1, 2022 at 9:3 Comment(11)
Guess it's being used by cout <<.Harmonious
Did you do a debug or release build? Even things like RVO often don't work in debug builds.Speed
Thanks for your reply. But unfortunately no. I remove that cout, still the same.Brita
I tried pass -DNDEBUG into, but the same. By the way, I also tried -O2, not working.Brita
did you turn on optimizations? -O2 / -O3 ?Belovo
AFAIK, std::function applies type erasure, which, generally, needs to allocate. Though it can also employ some form of small buffer/object optimization technique. Note that your lambda captures test, therefore, it has subobjects. BTW, what is your compiler?Bonefish
Could you run valgrind on cout << Test{}.Add(1, 2) << endl;? On my system I have 2 allocations with and without using std::function.Prairie
@DanielLangr I tried remove that capture, using this test case ``` FUNC_PROTO functor = [](int a, int b) {return a + b; };``` . The result is 1 byte heap allocation.Brita
Are you sure you aren't measuring allocation by the lambda? Have you tried : FUNC_PROTO functor = non member function?Speed
@Prairie On my system, your code cost 0 allocsBrita
Have you tested an empty main? I expect that we have one alloc at startup with the old implementation. Asking questions about an over 10 years old compiler and lib is a bit curious :-)Odoriferous
L
5

Older versions of libstdc++, like the one shipped by gcc 4.8.5, seem to only optimise function pointers to not allocate (as seen here).

Since the std::function implementation does not have the small object optimisation that you want, you will have to use an alternative implementation. Either upgrade your compiler or use boost::function, which is essentially the same as std::function.

Limelight answered 5/1, 2022 at 9:28 Comment(1)
Thanks for your reply. I did some test on the testcase you gave. The conclusion is this, from GCC version 5.1, small object optimization is supported in std::function. Thanks for your reply!Brita

© 2022 - 2025 — McMap. All rights reserved.