Lambda局部函数放循环内外的编译执行差异与性能影响
Lambda在循环内外的编译与执行差异分析
三段示例代码
Code 1(Lambda置于循环内,捕获循环变量)
#include <iostream> #include <vector> class Vector2d { public: double x, y; Vector2d(double x, double y) : x(x), y(y) { } }; int main() { const double FoV = 50.0; std::vector<Vector2d> vecOfVector2d; vecOfVector2d.emplace_back(Vector2d(2.0, 3.0)); vecOfVector2d.emplace_back(Vector2d(4.0, 5.0)); for (std::vector<Vector2d>::const_iterator i = vecOfVector2d.begin(); i != vecOfVector2d.end(); ++i) { const Vector2d vec = *i; double xpos = rand() % 100; // Calculated but varies in each iteration auto isBBoxInsideFOVHorizontal = [FoV, xpos, vec]()->bool{ std::cout << xpos << " " << vec.x << " " << vec.y << std::endl; return true; }; isBBoxInsideFOVHorizontal(); } return 0; }
Code 2(Lambda置于循环外,通过参数传递循环变量)
#include <iostream> #include <vector> class Vector2d { public: double x, y; Vector2d(double x, double y) : x(x), y(y) { } }; int main() { const double FoV = 50.0; std::vector<Vector2d> vecOfVector2d; vecOfVector2d.emplace_back(Vector2d(2.0, 3.0)); vecOfVector2d.emplace_back(Vector2d(4.0, 5.0)); auto isBBoxInsideFOVHorizontal = [FoV](const double xpos, const Vector2d vec)->bool{ std::cout << xpos << " " << vec.x << " " << vec.y << std::endl; return true; }; for (std::vector<Vector2d>::const_iterator i = vecOfVector2d.begin(); i != vecOfVector2d.end(); ++i) { const Vector2d vec = *i; double xpos = rand() % 100; // Calculated but varies in each iteration isBBoxInsideFOVHorizontal(xpos, vec); } return 0; }
Code 3(Lambda置于循环内,仅捕获全局常量,循环变量通过参数传递)
#include <iostream> #include <vector> class Vector2d { public: double x, y; Vector2d(double x, double y) : x(x), y(y) { } }; int main() { const double FoV = 50.0; std::vector<Vector2d> vecOfVector2d; vecOfVector2d.emplace_back(Vector2d(2.0, 3.0)); vecOfVector2d.emplace_back(Vector2d(4.0, 5.0)); for (std::vector<Vector2d>::const_iterator i = vecOfVector2d.begin(); i != vecOfVector2d.end(); ++i) { const Vector2d vec = *i; double xpos = rand() % 100; // Calculated but varies in each iteration auto isBBoxInsideFOVHorizontal = [FoV](const double xpos, const Vector2d vec)->bool{ std::cout << xpos << " " << vec.x << " " << vec.y << std::endl; return true; }; isBBoxInsideFOVHorizontal(xpos, vec); } return 0; }
问题解答
1. Code1和Code3是否会创建多个结构体或产生额外计算负担?
Lambda的本质是编译器在编译阶段生成的匿名functor类,这个类的定义只会生成一次,不会在每次循环时创建新的结构体类型。
- Code1每次循环会创建该functor的实例,因为捕获的
xpos和vec是循环内的变量,每次迭代值不同,需要构造新的实例来存储这些值。但这种构造开销非常小——捕获的都是简单数值或小型结构体,编译器甚至可能直接优化掉实例构造的过程,把逻辑内联到循环里。 - Code3的Lambda只捕获了不变的
FoV,每次循环创建的实例仅需存储这个常量值,开销比Code1更小,同样大概率会被编译器优化。
总体来说,所谓的"额外负担"在实际场景中完全可以忽略,只有在极端性能敏感的场景下才需要考虑,而此时编译器优化通常已经抹平了差异。
2. Code3中捕获始终不变的FoV是否会引发多次创建functor的问题?
不会。Functor的类型是编译时确定的,只会生成一次。每次循环创建的只是functor的实例,而非新的类型。
由于FoV是编译期可见的常量(const double且初始化为字面量),编译器会直接把FoV的值硬编码到Lambda的函数体中,甚至可能完全省略捕获的存储——也就是说,实际执行时可能根本不会有实例构造的动作,直接内联执行Lambda的逻辑。
3. 三段代码在编译/执行层面完全等价?
语法层面不等价,但开启编译器优化(如O2)后,执行效果和生成的机器码几乎完全一致。
- 语法上:Code1每次循环构造捕获了三个变量的functor实例并调用;Code2仅构造一次functor实例,每次循环传参调用;Code3每次循环构造仅捕获
FoV的functor实例并传参调用。 - 编译优化后:编译器会将Lambda的函数体直接内联到调用位置,此时三段代码的执行逻辑完全一致,生成的机器码没有区别。如果关闭优化,Code1和Code3会有微小的实例构造开销,但这种开销在绝大多数场景下不会影响性能。
内容的提问来源于stack exchange,提问作者Tetix
相关产品推荐
相关产品推荐

