Cost of self-executing C++11 lambdas
Asked Answered
O

2

6

Out of a window procedure, I'm writing a switch statement using self-executing lambdas, like this:

LRESULT CALLBACK proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg)
    {
        case WM_CREATE: return [&](WPARAM wp, LPARAM lp) {
            do_something(wp, lp);
            return 0;
        }(wp, lp);

        case WM_SIZE: return [&](HWND hWnd) {
            do_another_thing(hWnd);
            return 0;
        }(hWnd);
    }
    return DefWindowProc(hWnd, msg, wp, lp);
}

I believe compilers are free to optimize it pretty much the way they want, but generally speaking, would a compiler add much boilerplate code to this, comparing to not using these lambdas?

Could a compiler detect the redundant lambdas and remove them?

Ophir answered 4/10, 2016 at 23:33 Comment(14)
Just from curiosity: why? I mean what advantage do you seek from self-executing anonymous lambdas? You can't reuse them (because they are anonymous, not referenced in any other place), so...?Glazer
I think compiler would simply replace return []() {... return x; }() with {... return x; }. Seems trivial optimization. However this Q is more of an "opinion based", as there is no deterministic ansr.Dacia
@AdrianColomitchi Frivolous reason: because they are beautifully collapsed in Visual Studio IDE editor.Ophir
@Rodrigo you can just put them in {...} to get outlining to work. No need for this silliness.Antoine
@Dacia Non-deterministic compilers?Antoine
@DavidLively First thing I tried. It doesn't work in C++.Ophir
@Rodrigo See my answer. Sort of a duplicate, but I have a fancy screenshot which work in a comment.Antoine
Ok. Well, there's always '#pragma region'Antoine
@DavidLively Oh now I got what you mean: there's an option in VS to outline all statements. I disabled this some time ago because, well, it was too much outlining... as I said, it's a frivolous motivation, pure sugar. Anyway I still would like to know the implications of such lambda constructs. Your insights are appreciated, though.Ophir
@AdrianColomitchi I guess my answer also answers your question?Cutaneous
@Rodrigo "because they are beautifully collapsed in Visual Studio IDE editor. " :D thanks, this made my day, never could imagine.Glazer
@NirFriedman "I guess my answer also answers your question?" Not quite. While your answer is technically sound, I was looking for his personal reason (and must admit I was surprised).Glazer
@Rodrigo Anyway I still would like to know the implications of such lambda constructs. The biggest implication I can think of is that your future code maintainers will wonder why the code is written that way and what they're missing (how it behaves differently from the obvious solution).Halie
Good coding practice is to write short function. If you follow that and also have classes that follow Single Responsability Principle (SRP), you will never need to collapse code. While collapsing and #region might sometime be useful, depending a lot on it is a red flag that something is wrong. By the way you already call a function like do_something, why not simply return the value from that function and then write case WM_CREATE: return do_something(wp, lp);. Much cleaner to read and no useless collapsing.Pock
C
9

Optimization questions like this don't have a definite answer in the sense that the optimizer of a compliant compiler can do many, many things. However, in this case most modern optimizers are almost certainly going to inline the lambda, and generate the same assembly whether or not you use the lambda. Because lambdas have a unique type, the compiler can inline easily. Because the lambda is declared and immediately used and never assigned (a more common name is "immediately invoked/evaluated lambda instead of "self executing"), the compiler knows it can only be called once. So typically it will decide to inline.

To be sure, you can look at some assembly: https://godbolt.org/g/QF6WmR. As you can see, the generated assembly in this particular example is identical, but obviously it does not prove the general case.

In general, lambdas are considered low or zero cost abstractions in C++, if you think a lambda makes for the cleanest code then use one. If you need to you can always quickly verify the assembly is the same. Your reason for using the lambda that way is a bit unusual though; I wouldn't really consider code folding to be a good reason. A more common reason to use immediately evaluated lambdas is to be able to use const in situations where otherwise you can't:

int x;
try {
    x = foo();
}
catch (const ExceptionType& e) {
    x = bar();
} 

vs

const auto x = [] () {
    try {
        return foo();
    }
    catch (const ExceptionType& e) {
        return bar();
    }
}();

To persist x in the outside scope in traditional C++ code, we have to declare it first and then assign to it. By using a lambda that returns the value we want, we can declare and assign x at the same time, allowing it to be const.

Cutaneous answered 5/10, 2016 at 2:15 Comment(1)
The use of immediately invoked lambdas is pretty common in JavaScript land, where I've been wandering for the past 6 months. I guess I got too much used to them, since they're necessary to restrict scope in JS and used often. Oh I didn't know this site, it's great. Thank you.Ophir
G
5

The question is a bit odd. The compiler doesn't "remove" the lambdas, because the lambdas are in your source code, and the compiler doesn't modify your source code. What it does is emit machine code that produces the behaviour of the program you expressed in source code.

The compiler is free to emit as much or as little machine code as it likes as long as the result behaves the way that your program expresses.

A compiler certainly does not have to emit separate function bodies and jumps/calls between them if it can inline all the code into one place, and that is a commonly applied optimization.

Grounds answered 4/10, 2016 at 23:39 Comment(6)
When I said "remove the lambdas" I was referring to the machine code usually generated by a lambda construction. I mean, the compiler would ignore such a redundant lambda. So, rephrasing the question: would a compiler generally inline these lambdas, resulting in no extra cost?Ophir
The compiler doesn't "remove" the lambdas, because the lambdas are in your source code. I think OP didn't mean either about literal removal of lambda related source code. I read the last line in Q as: "Can compiler optimize away the extra machine code (if any) generated due to redundant lambdas?"Dacia
@iammilind: Unless you already know specific machine code that your compiler normally produces, that still doesn't make sense. And if you do, then you probably also know how to find out how the compiler translates the code in question. So whichever way you turn it, it doesn't really work. You can't both know and not know a thing.Grounds
@iammilind: To hammer home the point, the very idea of "removing machine code" is nonsense. That's just not how compilers work. If you don't know that, then there isn't really a useful answer. A better question might perhaps be whether the code in question results in worse performance than the simpler code. (And of course you know the answer to that kind of question.)Grounds
@Rodrigo: A compiler, generally? Yeah, sure, why not. (Would you ever be able to prove that I made a wrong statement?)Grounds
Perfect example of how not to answer a question. a) no one thought that the compiler modified the source code. b) your second paragraph just repeats a point the original asker already acknowledged in the question. Answer would have been good if it were just the third paragraph.Transmissible

© 2022 - 2024 — McMap. All rights reserved.