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

编译器是否可以内联 lambda 函数来提高效率,就像简单的标准函数那样?

例如:。

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

还是因为缺乏优化而导致效率下降?

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

46647 次浏览

在简单的情况下,如您的示例,与函数指针相比,使用 lambdas 可以获得更好的性能,参见

为什么编译器可以比普通函数更好地优化 lambdas?

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



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

海湾合作委员会

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

编译器使用各种启发式方法来确定是否内联函数。例如,编译器会考虑内联函数的大小和当前函数中已经完成的内联量。因此,源程序中看似无关紧要的更改可能导致-Winline 生成的警告出现或消失。

据我所知,如果函数没有内联声明,那么这个编译器标志很可能对 没有有帮助。尽管如此,知道它的存在还是好的,它部分地回答了你的第二个问题。

他指出的另一面旗帜是:

叮当 -Rpass=inline

发出优化报表的选项

优化报告在高层跟踪所有主要决策 通过编译器转换完成。例如,当内联 决定将函数 foo ()内联到 bar ()[ ... ]中

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

每当生成的程序集如此重要时,我都会亲自检查它。

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

但是这里有一个混淆的概念: 在 C + + 标准中,“ inline”是函数的 < em > 链接 ,也就是说,它是关于函数如何被 < em > 定义的声明,而不是它如何被调用。内联 可以定义的函数受益于编译器优化,通过优化内联调用这些函数。这是一个不同但高度相关的概念。

对于 lambdas,实际调用的函数是一个成员 operator(),它在编译器为 lambda 创建的匿名类中隐式定义为 inline。Lambda 的调用被转换为对其 operator()的直接调用,因此可以内联。我在另一个答案中更详细地解释了编译器如何创建 lambda 类型.

它取决于给予编译器的优化级别。以这两个语义相同的函数为例。一种是 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: : start 和 std: : end 调用也不是内联的)。

因此,尽管编译器可以(而且确实)在优化现代样式代码方面做得很好,但是在未经优化的调试构建中,这种代码可能或者很可能会导致性能下降。