为了便于理解,我使用了一个带有递归调用的局部lambda(tail recursive),运行这个(例如在http://cpp.sh/或https://coliru.stacked-crooked.com/上)总是显示lambda调用比其他解决方案慢很多。
我的问题是为什么会这样?
#include <iostream>
#include <chrono>
#include <functional>
//tail recursive lamda
long long int Factorial5(long long int n)
{
std::function<long long int(long long int,long long int)> aux
= [&aux](long long int n, long long int acc)
{
return n < 1 ? acc : aux(n - 1, acc * n);
};
return aux(n,1);
}
//tail recursive inline class
long long int Factorial6(long long int n)
{
class aux {
public: inline static long long int tail(long long int n, long long int acc)
{
return n < 1 ? acc : tail(n - 1, acc * n);
}
};
return aux::tail(n,1);
}
int main()
{
int v = 55;
{
auto t1 = std::chrono::high_resolution_clock::now();
auto result = Factorial5(v);
auto t2 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> ms = t2 - t1;
std::cout << std::fixed << "lamda(5)\tresult " << result
<< " took " << ms.count() << " ms\n";
}
{
auto t1 = std::chrono::high_resolution_clock::now();
auto result = Factorial6(v);
auto t2 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> ms = t2 - t1;
std::cout << std::fixed << "inner class(6)\tresult " << result
<< " took " << ms.count() << " ms\n";
}
}
输出:
lamda(5) result 6711489344688881664 took 0.076737 ms
inner class(6) result 6711489344688881664 took 0.000140 ms
3条答案
按热度按时间b0zn9rqh1#
lambda不是
std::function
,每个lambda都有自己唯一的类型,大致如下:所以原则上,lambda非常快,和任何函数或者非虚成员函数一样快。
缓慢的原因是
std::function
,它是lambda的类型擦除 Package 器,它将函数调用转换为虚拟调用,并可能动态分配lambda,这代价很高,并且妨碍了内联。要创建递归lambda,必须使用C++14泛型lambda并将lambda发送给它自己:
f0ofjuux2#
不管怎样,我的系统结果是:
这远没有那么激烈,尽管仍然有可测量的差异。
产生差异的原因是函数几乎不做任何工作,但是有非常非常多的函数调用。因此,任何函数调用开销都会支配结果。
std::function
有开销,这在度量中显示出来。注意,示例程序溢出
long long
,因此结果没有意义,因为行为完全未定义。正确的结果应该是大约6.689503e+198。理论上,UB也使运行时间测量的结果无效,但实际上不一定。mv1qrgav3#
lambda是一个未命名类型的对象,它调用另一个对象(
std::function
)的operator()
,std::function
调用lambda,...也就是说,存在通过对象的相互递归和间接性,这使得优化非常困难。
您的代码或多或少与下面的代码等效(我将类型缩短为
int
):这就更清楚地表明,有很多看不见的东西在发生。