News for hackers. Stuff that matters.
Re:Under the hood of C++ lambdas and std::function by hellofunk on Monday March 28, 2016 @05:19AM (#11373349)
>Lambdas are also awesome when it comes to performance. Because they are objects rather than pointers they can be inlined very easily by the compiler, much like functors. This means that calling a lambda many times (such as with std::sort or std::copy_if) is much better than using a global function. This is one example of where C++ is actually faster than C.

That's insight I've never seen anywhere before.

Re:Under the hood of C++ lambdas and std::function by Kristine1975 on Monday March 28, 2016 @05:59AM (#11373439)
It's also wrong. If the compiler inlines e.g. std::sort, it doesn't matter whether the comparator is a lambda or a pointer to a function: The compiler knows what it is and can inline it as well. The only exception is when the function is defined in a different compilation unit. Then the compiler has no access to its definition and thus cannot inline it into std::sort.

The "C++ with lambda is faster than C with function" comes from C's qsort function that takes a function pointer to a comparator and is not inlined by the compiler. This can result in it being slower than std::sort.

BTW since lambdas without capture decay to function pointers, you can pass such a lambda to qsort.

Re:Under the hood of C++ lambdas and std::function by gpderetta on Monday March 28, 2016 @02:32PM (#11376243)
In principle there is no difference, in practice until relatively recently some popular compilers would fail to inline calls via function pointers. The reason is that the inliner would need to run after constant propagation and after the HOF function itself, i.e. sort, has been inlined. With function objects and templates you get a distinct instance of sort for each callback type so the target is known statically even before any optimisation starts.
Re:Under the hood of C++ lambdas and std::function by stinos on Monday March 28, 2016 @05:50AM (#11373418)
I think there's truth in it but I'd still take it with a grain of salt, more like can be faster under the right circumstances. Inlining can not only be faster because there's no need for the function call prologue/epilogue etc but, probably more important, because the compiler sees all of the code and this opens doors for more optimizations. That's really important to realize; I recommend looking at the assembly generated by the compiler for loops etc from time to time. It's simply amazing what can be done. So if in similar code C++ can take advantage of it while C can't because it has to do an actual function call then the C++ version has advantages and might very well be faster (whereas otherwise both would produce roughly the same result and speed I guess, unless C++-only features like virtual functions etc used).
Re:Under the hood of C++ lambdas and std::function by exDM69 on Monday March 28, 2016 @09:15AM (#11374036)
> So if in similar code C++ can take advantage of it while C can't because it has to do an actual function call then the C++ version has advantages and might very well be faster

This is a matter of compilation units, not C vs. C++. If you put qsort() in a header file, and call it with a function pointer in the same compilation unit, it will get inlined and work exactly the same way as std::sort (which is in a header file since it's a template).

You can pretty easily verify this yourself by doing a C++ template and a C function with function pointers (in a header file!), compile and inspect the resulting assembly code.

Link-time optimization may relax the compilation unit requirement, but it's not very widely used yet.

Re:Under the hood of C++ lambdas and std::function by sebastic on Monday March 28, 2016 @06:01AM (#11373441)
His logic is wrong. It's not because they are objects, it's because std::sort and friends are function templates. They know what they are calling at compile time. Templates allow for compile-time polymorphism like this.

Because of compile-time polymorphism, you can pass in prvalue function pointers and objects alike and the compiler will be able to inline.

C doesn't have templates, therefore it doesn't have compile-time polymorphism, therefore it's much harder for the compiler to inline.

Compilers like GCC and Clang can inline in some runtime-polymorphism circumstances. See -flto and -fdevirtualize.

Re:Under the hood of C++ lambdas and std::function by exDM69 on Monday March 28, 2016 @09:26AM (#11374096)
> C doesn't have templates, therefore it doesn't have compile-time polymorphism, therefore it's much harder for the compiler to inline.

A C compiler can do the same inlining and optimizing as long as the called function (e.g. qsort) and the function that's pointed-to (e.g. the comparison function) are in the same compilation unit.

C++ templates do not add any magic here, std::sort() is simply faster because it's in a header file and qsort() is not. If you pass a function pointer to a function in a different object file to std::sort, it won't have a lot of advantages over qsort in terms of optimizations/performance (it still does know sizeof(T), though).

Link-time optimization should make standard qsort-type functions perform similarly to std::sort.

Re:Under the hood of C++ lambdas and std::function by sebastic on Monday March 28, 2016 @06:09AM (#11373461)
Also C++ is generally as fast as C. If anything, it's faster thanks to increased opportunities for inlining.

Whoever says C++ is slower than C is misinformed and/or overgeneralizing.

Professional C++ programmers know this already though.

Re:Under the hood of C++ lambdas and std::function by hellofunk on Monday March 28, 2016 @08:16AM (#11373785)
Well, much of C++ involves virtual function calls, and those are indeed slower than non-virtual calls. C doesn't have virtual calls, so that is perhaps one reason why C is generally assumed to be faster. That doesn't mean you must use virtual functions in C++; in fact, I don't use inheritance in my own code whatsoever, therefore I avoid the virtual calls but more importantly the restrictions that can creep into a tight OO hierarchy.

And std::function, from what I understand, is generally not as fast as a function pointer, a stored lambda, a function object, since it must do some run-time checks to see how to call what it is storing.

Re:Under the hood of C++ lambdas and std::function by kabdib on Monday March 28, 2016 @10:00AM (#11374272)
However, every sufficiently complex C program re-invents vtables (or has an even worse generic dispatching system), so you're often no worse off . . .
Re:Under the hood of C++ lambdas and std::function by mike_hock on Monday March 28, 2016 @09:38AM (#11374153)
The only virtual call that you necessarily get with polymorphism is the destructor call.

Whenever you need to make a function virtual in C++, you'd need to store a function pointer in an equivalent C design, somehow.

Re:Under the hood of C++ lambdas and std::function by hellofunk on Monday March 28, 2016 @11:17AM (#11374751)
Are you suggesting that C++ virtual functions have the same overhead as a C function pointer? I've never heard that; with the virtual call, there is the lookup + the invocation; perhaps a C app that stores function pointers in a hash map also has this sort of lookup, is that what you mean?
Re:Under the hood of C++ lambdas and std::function by mikeash on Monday March 28, 2016 @11:57AM (#11374997)
Vtable lookup in a typical C++ implementation is just loading a fixed offset in an array. In C terms, it's basically doing object->vtable[42]. In C code with function pointers, you're typically performing that exact same operation. You can go slightly faster if you store the function pointer in the object directly, at the cost of increased memory consumption per object if you have more than one function pointer.
Re:Under the hood of C++ lambdas and std::function by raphaelj on Monday March 28, 2016 @06:15AM (#11373473)
Don't forget about the templates too.

A generic collection is usually faster when implemented using templates instead of void*.

Re:Under the hood of C++ lambdas and std::function by jakub_h on Monday March 28, 2016 @07:03AM (#11373584)
Unless you use a DataDraw-like approach, I presume. I can't see templates doing similar amounts of data rearrangements.
Re:Under the hood of C++ lambdas and std::function by Kristine1975 on Monday March 28, 2016 @07:28AM (#11373638)
Re:Under the hood of C++ lambdas and std::function by jakub_h on Monday March 28, 2016 @07:44AM (#11373688)
Setting aside the cringe I experience whenever supposed principles of "OOP" are mentioned by the author, that page painfully reminds me of the that old joke about bolting four extra legs onto a dog... Furthermore, both a broader comparison (for more complex data and processing) and the technique I mentioned seems to be absent. However, I suppose it shows how to reclaim at least some benefits of better data arrangement even if the language isn't a good fit.
Re:Under the hood of C++ lambdas and std::function by Rexxar on Monday March 28, 2016 @07:46AM (#11373693)
Did you speak of this "datadraw.sourceforge.net" ? It's the first time I heard of it. Have you used it ?
Re:Under the hood of C++ lambdas and std::function by jakub_h on Monday March 28, 2016 @08:03AM (#11373751)
For a number of reasons, I'm not particularly fond of this specific implementation but I'm seriously considering porting its principles into something else (where they might work even better).
Re:Under the hood of C++ lambdas and std::function by mikeash on Monday March 28, 2016 @11:54AM (#11374984)
The only reason C++ has increased opportunities for inlining is because C++ often requires code to be placed in header files. If you wrote C in this style, you'd have the same opportunities for inlining, it's just that nobody writes it that way, because we generally prefer separately compiled libraries.
Re:Under the hood of C++ lambdas and std::function by p0nce on Monday March 28, 2016 @10:53AM (#11374584)
I'd even say it's generally faster because you can use integer as template arguments for enforced constant folding. Typically in video work: make a code path specialized when block width is 8, 16, 32 etc. Something that a C compiler can never do unless you use huge macros.