绑定lambda的速度(通过std :: function)与functor struct的operator()

时间:2014-09-23 00:54:51

标签: c++ performance c++11 lambda

auto lam = [](int a, int b, int c) { return a < b && b < c; };

struct functor {
  int a;
  int b;
  bool operator()(int n) const { return a < n && n < b; }
};

在第一版中,我们

std::vector<std::function<bool (int)>> lamvals;
// get parameters and for each
lamvals.emplace_back(std::bind(lam, a, std::placeholders::_1, b));

替代方案是

std::vector<functor> lamvals;
// get parameters and for each
lamvals.emplace_back(functor{a, b});

在这两种情况下,我们都有一个简单的迭代

    return std::any_of(lamvals.cbegin(), lamvals.cend(),
            [n](const decltype(lamvals)::value_type & t){return t(n);});

我看到速度差为3:1,绑定的lambda速度较慢。仿函数几乎与存储整数对和硬编码测试一样快。显然,硬编码对于生产代码没那么有用,因为会有几个功能在起作用,而不仅仅是在它们之间。但是,我可以选择许多仿函数或许多lambdas。后者是更少的代码行,看起来更干净,但我认为我不能承受这种速度差异;这段代码处于关键循环中。

我正在寻找加速建议。

1 个答案:

答案 0 :(得分:16)

两种情况之间的区别基本上是使用仿函数,编译器确切地知道在编译时将调用什么,因此可以内联函数调用。有趣的是,Lambdas也有一种独特的类型。这意味着,当你使用lambda时,在编译类型(因为编译器必须知道所有类型),被调用的函数已经知道,所以可以进行内联。另一方面,函数指针仅基于其签名的类型。必须知道签名,以便可以适当地调用它并从中返回,但除了编译器所涉及的功能指针可以在运行时指向任何内容之外。关于std :: function也是如此。

当你在一个std :: function中包装lambda时,你会从编译器的角度删除lambda的类型。如果这听起来很怪异/不可能,那就这样想吧:因为固定类型的std :: function可以用相同的签名包装任何可调用的,编译器无法知道其他一些指令不会单独出现并改变std :: function包装的内容。

此链接http://goo.gl/60QFjH显示了我的意思(顺便说一下,Godbolt页面非常方便,我建议熟悉它)。我在这里写了三个类似于你的例子。第一个使用std :: function包装lambda,第二个使用funl,第三个是使用decltype的裸lambda(unwrapped)。您可以查看右侧的程序集,看看后两者是否内联,而不是第一行。

我的猜测是你可以使用lambdas做同样的事情。您可以使用a和b的lambdas进行基于值的捕获,而不是绑定。每次将lambda推回向量时,请适当修改a和b,然后瞧。

虽然在风格上,我实际上强烈认为你应该使用结构。它发生了什么事情要清楚得多。事实上,你似乎想要在一个地方捕获a和b,并在另一个地方测试c,这意味着你的代码中不仅仅在一个地方使用它。为了换取类似的2行代码,您可以获得更易读,更易于调试和更具可扩展性的内容。