Consider the following code:
#include <iostream>
#include <functional>
std::function<void ()> f()
{
int x = 666;
return [&] { std::cout << x << std::endl; };
}
int main()
{
f()();
return 0;
}
$ g++ -o main -std=c++14 -Wall main.cpp
$ ./main
666
-O1
$ g++ -o main -O1 -std=c++14 -Wall main.cpp
$ ./main
0
-O2
$ g++ -o main -O2 -std=c++14 -Wall main.cpp
main.cpp: In function ‘int main()’:
main.cpp:7:31: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
return [&] { std::cout << x << std::endl; };
^
$ ./main
32699
-O3
$ g++ -o main -O3 -std=c++14 -Wall main.cpp
main.cpp: In function ‘int main()’:
main.cpp:7:31: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
return [&] { std::cout << x << std::endl; };
^
$ ./main
32528
>g++ -o main.exe -std=c++14 -Wall main.cpp
>.\main.exe
666
-O1
>g++ -o main.exe -O1 -std=c++14 -Wall main.cpp
>.\main.exe
0
-O2
>g++ -o main.exe -O2 -std=c++14 -Wall main.cpp
>.\main.exe
0
-O3
>g++ -o main.exe -O3 -std=c++14 -Wall main.cpp
>.\main.exe
0
>cl /EHsc main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.27.29111 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 14.27.29111.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
>.\main.exe
8402693
/O1
>cl /EHsc /O1 main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.27.29111 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 14.27.29111.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
>.\main.exe
666
/O2
>cl /EHsc /O2 main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.27.29111 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 14.27.29111.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
>.\main.exe
666
As you can see, with different compilers and optimization levels, the program outputs 666
, 0
, or a garbage value. Why does the above happen?
In which case it’s not a problem to call a Lambda function directly across service boundaries. And what you lose in flexibility you gain in performance and cost efficiency as you cut out a layer from the execution path. There are also other exceptions to keep in mind.
This impacts the end-to-end latency. The 2nd Lambda invocation carries an extra cost for the invocation request. Since Lambda durations are paid in 100ms blocks, so you will pay for the amount of “roll-up” time for both caller and callee functions. You will pay for the idle wait time while the caller function waits for a response from the callee.
If you utter the words “I call a Lambda function from another Lambda function” you might receive a bunch of raised eyebrows. It’s generally frowned up for some good reasons, but as with most things, there are nuances to this discussion. In most cases, a Lambda function is an implementation detail and shouldn’t be exposed as the system’s API.
Since Lambda durations are paid in 100ms blocks, so you will pay for the amount of “roll-up” time for both caller and callee functions. You will pay for the idle wait time while the caller function waits for a response from the callee.
You capture x
by reference in the lambda and after leaving f()
it becomes a dangling reference as x
gets destroyed. You have a classic UB. To avoid it you can capture x
by value by writing [x]
or [=]
instead of [&]
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With