编译器内联lambda函数能否/可以提高效率,就像使用简单的标准函数一样?
e.g。
std::vector<double> vd;
std::for_each(vd.begin(), vd.end(), [](const double d) {return d*d;});
或者由于缺乏优化而导致效率下降?
第二个问题:在哪里可以检查我使用的编译器是否优化了内联函数的调用,这些函数被发送到算法?我的意思是,如果函数 - 而不是函数对象 - 被发送到算法,最后一个获取指向函数的指针,并且一些编译器优化指向内联函数的指针而其他编译器没有。
答案 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调用都没有。)
因此,虽然编译器可以(并且确实)在要求这样做时优化现代样式代码时做得非常出色,但在未经优化的调试版本中,这种代码可能会或可能会受到性能损失。