lambdas是否像C ++中的函数一样内联?

时间:2013-04-10 15:57:06

标签: c++ c++11 lambda stl

编译器内联lambda函数能否/可以提高效率,就像使用简单的标准函数一样?

e.g。

std::vector<double> vd;
std::for_each(vd.begin(), vd.end(), [](const double d) {return d*d;});

或者由于缺乏优化而导致效率下降?

第二个问题:在哪里可以检查我使用的编译器是否优化了内联函数的调用,这些函数被发送到算法?我的意思是,如果函数 - 而不是函数对象 - 被发送到算法,最后一个获取指向函数的指针,并且一些编译器优化指向内联函数的指针而其他编译器没有。

3 个答案:

答案 0 :(得分:32)

在简单的情况下,就像你的例子一样,你应该期望使用lambdas比使用函数指针更好的性能,参见

Why can lambdas be better optimized by the compiler than plain functions?

正如其他人已经指出的那样,无法保证你的电话会被内联,但你有更好的机会与lambdas。检查调用是否已内联的一种方法是检查生成的代码。如果您使用的是gcc,请将-S标志传递给编译器。当然,它假定您可以理解汇编代码。



2018年9月11日更新: Vipul Kumar在他的编辑中指出了两个编译器标志。

GCC -Winline

  

如果无法内联声明为内联的函数,则发出警告。即使使用此选项,编译器也不会警告系统头中声明的内联函数失败。

     

编译器使用各种启发式方法来确定是否内联函数。例如,编译器会考虑内联函数的大小以及当前函数中已经完成的内联量。因此,源程序中看似微不足道的变化可能会导致-Winline产生的警告出现或消失。

据我所知,如果你的函数没有内联声明,那么这个编译器标志很可能有用。然而,知道它存在并且部分回答你的第二个问题是很好的。

他指出的另一面旗帜是:

Clang -Rpass=inline

  

发布优化报告的选项

     

优化报告从高层次上追踪所有重大决策   由编译器转换完成。例如,当内联   决定将函数foo()内联到bar()[...]

我自己没有使用过这个,但根据文档可能对您的用例有用。

我会亲自检查生成的装配,只要它很重要。

答案 1 :(得分:23)

首先:C ++中lambda的设计的全部意义在于它们与函数调用相比没有开销。这尤其包括可以内联调用它们的事实。

但这里存在一些概念混淆:在C ++标准中,“inline” is the linkage of a function, i.e. it is a statement about how a function is defined,而不是如何被调用。内联定义的函数可以从编译器优化中受益,通过该编译器优化可以内联对此类函数的调用。这是一个不同但高度相关的概念。

在lambdas的情况下,被调用的实际函数是成员operator(),它在编译器为lambda创建的匿名类中隐式定义为inline。 lambda的调用被转换为对operator()的直接调用,因此可以内联。 I’ve explained how the compiler creates lambda types in more detail in another answer

答案 2 :(得分:12)

这取决于给编译器的优化级别。例如,这两个函数在语义上是相同的。一个是C ++ 11风格,另一个是C风格。

void foo1 (void)
{
    int arr[100];
    std::generate(std::begin(arr), std::end(arr), [](){return std::rand()%100;});
}

void foo2 (void)
{
    int arr[100];
    for (int *i = arr; i < arr+100; i++) *i = std::rand()%100;
}

使用gcc -O4进行编译会发出两个函数非常相似(不相同但复杂等效)的代码。

然而,在编译未优化时没有内联lambda(并且std :: begin和std :: end调用都没有。)

因此,虽然编译器可以(并且确实)在要求这样做时优化现代样式代码时做得非常出色,但在未经优化的调试版本中,这种代码可能会或可能会受到性能损失。