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

Ill*_*kyi 45 c++ lambda stl c++11

编译器内联lambda函数是否可以提高效率,因为它可以使用简单的标准函数?

例如

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

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

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

Ali*_*Ali 35

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

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

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



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

GCC -Winline

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

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

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

他指出的另一面旗帜是:

-Rpass=inline

发出优化报告的选项

优化报告从高级别跟踪编译器转换完成的所有主要决策.例如,当内联器决定将函数foo()内联到bar()[...]

我自己没有使用过这个,但基于文档,它可能对您的用例有用.

我个人检查生成的组件,只要它很重要.

  • 任何人都可以阅读汇编代码,问题是如果你能理解它. (25认同)
  • 匿名的downvotes没有帮助任何人.这个问题出了什么问题? (6认同)

Kon*_*lph 26

首先:C++中lambdas设计的重点在于它们与函数调用相比没有开销.这尤其包括可以内联调用它们的事实.

但是这里存在一些概念的混淆:在C++标准中,"内联"是函数的链接,即它是关于如何定义函数的声明,而不是如何调用它.内联定义的函数可以从编译器优化中受益,通过该编译器优化内联调用此类函数.这是一个不同但高度相关的概念.

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

  • 有趣的是,这是我最不赞成的答案之一,但没有人解释它有什么问题。 (2认同)
  • 我认为这应该放在最上面,因为它被称为成员operator()使得可以很容易地设置关于内联的期望。 (2认同)
  • @Andry再次,您不知道您在说什么,特别是您的第一条评论毫无意义。您会得到关于C ++(特别是关于链接)的“根本”事实,并且,除非您进一步了解该语言的基础知识和编译器/链接器体系结构,否则您将不了解Lambda在C ++中的工作方式。 (2认同)

小智 13

它取决于给编译器的优化级别.例如,这两个函数在语义上是相同的.一种是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;
}
Run Code Online (Sandbox Code Playgroud)

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

但是,在编译未优化时,lambda不会内联(并且std :: begin和std :: end调用都不是).

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

  • @DrYak我总是用"-O9999"编译,因为我还没找到[限制破坏者](http://finalfantasy.wikia.com/wiki/Break_Damage_Limit)...当然我开玩笑.从技术上讲,"-O3"以上的所有内容都转换为"-O3". (13认同)
  • 顺便说一句,gcc中没有实际的"-O4". (12认同)
  • @艾米丽L。那“-O-1”呢?它是否下溢回绕到最大值?:) (2认同)