绑定lambda的速度(通过std ::函数)vs操作符struct() [英] Speed of bound lambda (via std::function) vs operator() of functor struct

查看:148
本文介绍了绑定lambda的速度(通过std ::函数)vs操作符struct()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  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.emplace_back(std :: bind(lam,a,std :: placeholders :: _ 1,b));

替代方法是

  std :: vector< functor> ; 
//获取参数并为每个
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更慢。函数几乎和存储整数对和硬编码测试一样快。显然,硬编码对于生产代码不是有用的,因为将会有几个功能在使用,而不只是在之间。然而,我可以去许多functor或许多lambdas。后者是更少的代码行,看起来更干净,但我不认为我能承受这个速度差;

解决方案



我正在寻找加速建议。 div>

这两种情况的区别是,从根本上说,使用函子,编译器会准确地知道在编译时将调用什么,因此函数调用可以被内联。 Lambdas,有趣的是,也有一个独特的类型。这意味着,当你使用lambda时,在编译类型(因为编译器必须知道所有类型)被调用的函数是已知的,所以可能发生内联。另一方面,函数指针是基于其签名的类型。签名必须是已知的,以便它可以被适当地调用和返回,但除了编译器,函数指针可以指向运行时的任何东西。 std :: function也是如此。



当在一个std ::函数中包装lambda时,你会从编译器的角度删除lambda的类型。如果这听起来很奇怪/不可能,可以这样想:因为一个固定类型的std ::函数可以包装任何可调用的相同的签名,编译器没有办法知道一些其他指令不会孤单和改变



此链接, http://goo.gl / 60QFjH ,显示了我的意思(顺便说一下,godbolt页面是非常非常方便,我建议认识它)。我在这里写了三个例子与你的类似。第一个使用std ::函数包装一个lambda,第二个函子,第三个裸体lambda(unwrapped),使用decltype。你可以看看右边的程序集,看到后两者都内联,但不是第一个。



我的猜测是,你可以使用lambdas做完全相同的事情。而不是绑定,你可以只做基于价值的捕获与a和b的lambdas。每次你将lambda推回到向量中,请适当地修改a和b。



然而,我觉得应该使用struct。这是更清楚发生了什么。只是你似乎想捕获a和b在一个地方,并测试c在另一个,这意味着这是在你的代码中使用不只是一个地方。换句话说,你可以使用2行代码,这样你就可以更容易阅读,更容易调试,更易扩展。


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; }
};

In version one, we

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

The alternative is

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

In both cases we have a simple iteration

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

I am seeing a speed difference of 3:1, with the bound lambda slower. The functor is almost as fast as storing integer pairs and hardcoding the tests. Obviously, hardcoding is not as useful for the production code, because there will be several functions in play, not just between. However, I can go with either many functors or many lambdas. The latter is fewer lines of code and looks cleaner, but I don't think I can afford that speed difference; this code is in a critical loop.

I am looking for speedup suggestions.

解决方案

The difference between the two cases is fundamentally that with the functor, the compiler knows exactly what will be called at compile time, so the function call can be inlined. Lambdas, interestingly enough, also have a unique type. This means again, when you use a lambda, at compile type (since the compiler must know all types) the function being called is already known, so inlining can occur. On the other hand, a function pointer is type based only on its signature. The signature must be known so that it can be called to and returned from appropriately, but other than that a function pointer can point to anything at run-time, as far as the compiler is concerned. The same is true about std::function.

When you wrap the lambda in a std::function, you erase the type of the lambda from a compiler perspective. If this sounds weird/impossible, think of it this way: since a std::function of a fixed type can wrap any callable with the same signature, the compiler has no way of knowing that some other instruction won't come alone and change what the std::function is wrapping.

This link, http://goo.gl/60QFjH, shows what I mean (by the way, the godbolt page is very very handy, I suggest getting acquainted with it). I wrote three examples here similar to yours. The first uses std::function wrapping a lambda, the second a functor, the third a naked lambda (unwrapped), using decltype. You can look at the assembly on the right and see that both of the latter two get inlined, but not the first.

My guess is that you can use lambdas to do exactly the same thing. Instead of bind, you can just do value based capture with the lambdas of a and b. Each time you push back the lambda into the vector, modify a and b appropriately, and voila.

Stylistically though, I actually strongly feel you should use the struct. It's much clearer what's going on. The mere fact that you are seeming to want to capture a and b in one place, and test against c in another, means that this is used in your code in not just one place. In exchange for like, 2 extra lines of code, you get something more readable, easier to debug, and more extensible.

这篇关于绑定lambda的速度(通过std ::函数)vs操作符struct()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆