Binary size when using std::function with lambda expression

Hello,

When using std::function instead of function pointer (see the sample of code) on Mac, we get a huge difference between size of generated binary (80ko when using function pointer and 157ko whit std::function).

I use Xcode to build debug with -O0 -std=gnu++14 and libc++.

It doesn’t seem to have the same result on windows or linux.

Is it something normal ?

Here’s the code sample :

#include

#include

int main(int argc, const char * argv) {

std::function<void()> f = {std::cout << “LOG2\n”;};

// void (*f)() = {std::cout << “LOG3\n”;};

f();

return 0;

}

Thanks,

Cyril

image0b65ab.JPG
Cyril Makloufi
Développeur

Email : Cyril.Makloufi@4d.com
Web : www.4d.com

4D SAS
66 route de Sartrouville
Parc Les Erables - Batiment 4
78230 Le Pecq - France

Standard : +33 1 30 53 92 00

Hi Cyril,

https://godbolt.org/z/vv3xWP generated 467 lines of assembler with clang trunk and -O0 (on Linux, I suppose), versus 58 lines for the function pointer.

With -O2 it was 76 lines vs 20 lines.
(Nine lines are always used to initialize iostream and set up its destruction)

There may be extra code linked in from libc++ for std::function.

Csaba

image0b65ab.JPG

libcxx-dev@lists.llvm.org might be a good place to send this as well, since it’s possible it’s a libc++ thing.

image0b65ab.JPG

This is expected. std::function<void()> is a library type. It does “type erasure,” a C++ technique which you can learn about here:
https://www.youtube.com/watch?v=tbUCHifyT24 (“Back to Basics: Type Erasure”, CppCon 2019)

Contrast with core-language lambda expressions:
https://www.youtube.com/watch?v=3jCOwajNch0 (“Back to Basics: Lambdas from Scratch”, CppCon 2019)

You can do a lot of things with std::function<void()> f that you can’t do with void (*f)(); for example, std::function<void()> f can hold a copy of a lambda which itself has captures.

int i = 42;
std::function<void()> f = & { std::cout << i; }; // OK
void (*g)() = & { std::cout << i; }; // Ill-formed

You pay for this flexibility in code size (and speed) (and, in many cases, heap-allocations).

Additionally, std::function is notoriously inefficient even as type-erased types go. This is partly because the paper standard requires it to do so much (e.g. the .target_type() method); and partly because its implementation was done ~15 years ago and maybe we’d like to apply some lessons learned since then, but that would break ABI.
https://quuxplusone.github.io/blog/2019/01/06/hyper-function/

HTH,
Arthur