C++ Pirate: Lambda vs Bind (二)

2014-11-23 22:54:04 · 作者: · 浏览: 25
accumulate_lambda);
run_test("Accumulate (bind) ", &test_accumulate_bind);
run_test("Accumulate (bound lambda)", &test_accumulate_bound_lambda);
}事不宜迟,我们先来看看使用gcc 4.4.2 -O3编译并且在Inter Core i7 Q740机器上运行的结果:Accumulate (lambda) 7
Accumulate (bind) 4401849
Accumulate (bound lambda) 4379315每当我在做性能测试时看到运行结果耗非常悬殊时我都会反汇 编程序看看编译器到底做了什么。(gdb) disassemble test_accumulate_lambda
Dump of assembler code for function _Z22test_accumulate_lambdav:
0x0000000000400e70 <+0>: movabs $0x6f05b59b5e49b00,%rax
0x0000000000400e75 <+5>: retq
End of assembler dump.在经过编译器优化之后,整个函数仅仅是将0x6f05b59b5e49b00(十进制值为:499999999500000000)移动到了rax寄存器中就返回了。编译器非常智能的知道了我们仅仅是对0到1000000000之间的数字求和并直接帮我们进行了代码替换的优化,另我影响深刻的是编译器竟然可以做到这点并且非常合理。函数的内容对do_test_loop函数的实例是静态已知,所以编译器将原有的代码转化成了如下所示的代码:[cpp] view plaincopyprint uint64_t test_accumulate_lambda() { uint64_t x = 0; // do_test_loop: for (uint64_t i = 0; i < 1000000000; ++i) x += i; return x; } uint64_t test_accumulate_lambda()
{
uint64_t x = 0;
// do_test_loop:
for (uint64_t i = 0; i < 1000000000; ++i)
x += i;
return x;
}任何优秀的编译器都将对其进行优化。我认为要从这个简单例子中获取的最重要的信息是:编译器知道lambda函数是具有静态性的,因此你可以放心的使用lambda函数而不必担心它性能。那么我们调用的std::function又是怎样的一个过程呢?在这里它的多态性让我们很难去剖析,当函数do_test_loop被函数std::function实例化时,编译器并不知道func的行为,因此它能做任何事情(它只是std::function的入口点)。std::bind和lambda表达式之间的不同之处是极其细微的。如果你多次的运行测试用例,在我的电脑里lambda表达式的总会比std::bind的快一点,但是这些数据并不具有统计学的意义。这种性能在以后很有可能在不同的机器上会发生改变,如果我要猜测我会说这有std::reference_wrapper的作用。下面让我们来看看两个函数的堆栈。std::bind
#0 test_accumulate_bind_function (x=@0x7fffffffe5d0, i=0) at lambda_vs_bind.cpp:106
#1 0x0000000000401111 in operator() (__args#0=0, this=
) at /usr/local/include/gcc-4.6.2/functional:2161
#2 do_test_loop > (func=, upper_limit=) at lambda_vs_bind.cpp:93
#3 test_accumulate_bind () at lambda_vs_bind.cpp:115
#4 0x0000000000401304 in run_test (name=, func=0x401080 ) at lambda_vs_bind.cpp:84
#5 0x0000000000401411 in main () at lambda_vs_bind.cpp:136
Lambda Expression
#0 std::_Function_handler >::_M_invoke(const std::_Any_data &, unsigned long) (__functor=..., __args#0=0) at /usr/local/include/gcc-4.6.2/functional:1778
#1 0x0000000000400fa9 in operator() (__args#0=0, this= at /usr/local/include/gcc-4.6.2/functional:2161
#2 do_test_loop > (func=, upper_limit=) at lambda_vs_bind.cpp:93
#3 test_accumulate_bound_lambda () at lambda_vs_bind.cpp:126
#4 0x0000000000401304 in run_test (name=, func=0x400f20 ) at lambda_vs_bind.cpp:84
#5 0x000000000040143e in main () at lambda_vs_bind.cpp:140它们的不同之处仅仅是在std::function的operator()函数调用,为了正真发生了什么,我们来快速的看一下g++ 4.6.2的std::function是怎么实现的:[cpp] view plaincopyprint template class function<_Res(_ArgTypes...)> : public _Maybe_unary_or_binary_function<_Res, _ArgTypes...>, private _Function_base { // a whole bunch of implementation details private: typedef _Res (*_Invoker_type)(const _Any_data&, _ArgTypes...); _Invoker_type _M_invoker; }; template
class function<_Res(_ArgTypes...)>
: public _Maybe_unary_or_binary_function<_Res, _ArgTypes...>,
private _Function_base
{
// a whole bunch of implementation details
private:
typedef _Res (*_Invoker_type)(const _Any_data&, _ArgTypes...);
_Invoker_type _M_invoker;
};最令我感兴趣的是std::function没有使用virtual而是使用了一个函数指针。这样做有一些优势所在,这样能够让你在使用std::function时不需要处理指针和引用——