在G ++中用C++0x Lambda Expression玩弄了一下,我想知道在没有使用lambda函数的情况下,与一般/特定情况相比,性能有多好。
是否有人知道或多或少全面讨论lambda表达性能或者在开发过程中应该避免使用它们的情况?
答案 0 :(得分:28)
如果你考虑使用operator ()
作为替代方法来定义结构的老派方式,那么就没有任何区别,因为lambdas几乎与之相当。语法上更方便。让我们等一个更完整的答案...
答案 1 :(得分:16)
当第一次遇到lambda表达式时,很多人都会产生模糊的印象,即创建这些函数时会发生一些运行时编译魔术。具体来说,如果你有一个函数返回一个新生成的函数作为结果,看起来每次调用周围函数时都会“创建”返回的函数。但这是错误的 - 一个lambda表达式(在任何语言中都是如此)包含一些可以像任何其他代码一样编译的代码,并且它们都是静态发生的,没有任何成本需要留给运行时。
唯一的问题是关闭的变量会发生什么,但这并不妨碍这样的编译 - 创建一个闭包,你只需将闭包数据(这些变量)与一个指向的指针静态配对编译代码。这在性能方面的含义是,根本不应该有性能损失 - 封闭变量。即使有封闭的变量也没有成本 - 任何其他方法来解决你所遇到的任何问题都需要以某种方式打包这些值,所以分配的成本是相同的,无论你如何保留它(明确地,或者隐含在封闭的变量中)。如果替代解决方案不需要打包某些值,那么也不需要使用闭包来关闭它们。这与执行代码所需的本地分配非常相似 - 无论代码是来自具有本地范围的闭包还是来自需要相同类型的本地状态的其他范围,这显然都是相同的。
同样,这是所有带闭包的语言中的东西,并且C ++代码没有理由遇到其他语言没有的性能问题。 C ++ lambda表达式中的一个奇怪之处是需要指定您关闭的变量,而在大多数其他语言中,您只需将所有内容都默认关闭。这看起来似乎给了C ++代码一些优势,可以更好地控制需要与闭包一起打包的东西 - 但这对于编译器来说很容易自动完成,没有明确的注释。它导致函数式语言编译器最常见的事情之一 - “lambda提升” - 其中一个函数被有效地提升到顶层,避免了在运行时创建闭包的需要(如果不需要的话)。例如,如果你写(使用一些类似JS的伪代码):
function foo(x) {
return function(y) { return y+3; }
}
然后很容易(对于编译器和人类)看到返回的函数不依赖于x
,编译器现在可以解除它,就像你写的那样:
function generated_name1234(y) { return y+3; }
function foo(x) {
return generated_name1234;
}
当只关闭某些值时,会使用类似的技术。
但这是分歧的。最重要的是,lambda表达式不会产生一些性能损失 - 闭合变量与否。
(至于使用operator()
比较lambda表达式,我不确定大多数C ++编译器会做什么,但lambdas应该更快,因为任何方法调用都不需要运行时调度。即使lambdas实现为具有()
运算符的匿名类,上述技术也适用于这种情况,这意味着可以编译调度机制,这意味着它也不应该有额外的成本,使它成为类似于一个特殊情况,其中匿名类对于高效编译来说是微不足道的。)
答案 2 :(得分:5)
我没有看到任何设计理由为什么闭包应该是比同等函数更小的表演者与相同数量和大小的传递参数
即使是捕获所有上下文变量的闭包也应该能够优化掉lambda中实际使用的上下文变量。
特定的上下文变量(通过值或引用捕获)将需要在实例化时初始化的一些存储,这发生在执行期间首次找到lambda的位置。但是这个存储不需要堆,堆栈分配完全没问题。
一个lambda 与正常函数完全相同,唯一的区别是完全不正确的;它在其他函数中定义,并且可以捕获一些外部变量,这些变量被编译为附加的上下文参数。 context参数可能在定义lambda的位置进行类似POD的初始化。
如果特定的编译器(即:g ++或clang)与上面的行为发生冲突,则表明执行不良。 clang能够轻松扩展设计传递的优化,因此从长远来看,任何这样的缺点都应该更容易解决,比如g ++
如果不使用上下文变量,则底线为,lambda(应该)与常规自由函数完全无法区分
答案 3 :(得分:2)
正如其他人提到的那样,没有理由为什么编译器生成的lambdas闭包应该与手写的闭包有任何不同。为了验证它,我只是用手写类改变了解析器中使用的lambdas。它们都只包含几行紧密代码并执行了数百万次,因此每次性能变化都会立即引起注意。结果 - 完全相同的执行时间。所以不,表现没有差异。
答案 4 :(得分:1)
我们开始在某些情况下(游戏环境)开始避免使用lambdas,因为创建的闭包(如果它已捕获的值)具有关联的new / delete来保存任何捕获的值。虽然在许多环境中这不是问题,但我们一直在削减微秒以获得最佳性能。 Lambdas(带有封闭变量的那些)在我们的分析中占有很高的位置,并且是最先走的。