GCC中lambda函数的速度有多快

How fast are lambda functions in GCC

本文关键字:速度 lambda 函数 GCC      更新时间:2023-10-16

在G++中玩过C++0x Lambda表达式之后,我想知道在一般/特定情况下,与不使用Lambda函数的其他方法相比,性能会有多好。

有人知道关于lambda表达式性能的或多或少全面的讨论吗?或者在开发过程中,尽管更舒适,但应该避免它们的情况吗?

如果你考虑用operator ()定义结构的老派方法作为替代方法,那么不会有任何区别,因为lambda与之相当。只是在语法上更方便。让我们等待更完整的答案。。。

当第一次遇到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表达式并不会导致性能损失——不管是否是封闭变量。

(至于将lambda表达式与使用operator()进行比较,我不确定大多数C++编译器会做什么,但lambda应该更快,因为任何方法调用都不需要运行时调度。即使lambda被实现为带有()运算符的匿名类,上述技术也可以应用于这种情况,这意味着可以编译掉调度机制d意味着它不应该有额外的成本,这使得它类似于一种特殊情况,即匿名类对于高效编译来说微不足道。)

在传递参数的数量和大小相同的情况下,我看不出闭包的性能不如等效函数的任何设计原因

即使是捕获所有上下文变量的闭包也应该能够优化掉lambda中实际使用的上下文变量。

特定的上下文变量(通过值或引用捕获)将需要在实例化时初始化一些存储,这发生在执行过程中首次找到lambda时。但这种存储不需要是堆,堆栈分配非常好。

lambda与正则函数完全相同,唯一的区别完全是sintactical;它在其他函数中定义,并且可以捕获一些外部变量,这些变量被编译为一个附加的上下文参数。上下文参数可能在定义lambda的点处具有类似POD的初始化。

如果一个特定的编译器(如g++或clang)的行为与上面的行为相冲突,这是一个错误实现的警告信号。clang能够很容易地扩展设计传递的优化,因此从长远来看,与g++相比,任何这样的缺点都应该更容易解决

底线是如果不使用上下文变量,lambda(应该)与编译器的常规自由函数完全无法区分

我们开始避免在某些情况下(游戏环境)使用lambda,因为创建的闭包(如果它捕获了值)有一个相关的新/删除来保存任何捕获的值。虽然在许多环境中,这不是一个问题,但我们的业务是一次减少微秒,以获得最佳性能。Lambdas(带有封闭变量的Lambdas)在我们的评测中占有重要地位,是最早推出的。

正如其他人所提到的,编译器生成的由lambdas生成的闭包在性能上与手工编写的闭包没有任何区别。为了验证它,我只是用手工编写的类更改了解析器中使用的lambda。它们都只包含几行紧凑的代码,并且执行了数百万次,因此性能的每一次变化都会立即引起注意。结果——完全相同的执行时间。所以不,性能没有差别。