测试代码是这样的,计时器将输出销毁时经过的时间
uint64_t res1 = 0, res2 = 0;
void test_accumulate_bind_function(uint64_t& x, uint64_t i)
{
x += i;
}
uint64_t res1 = 0, res2 = 0, res3 = 0, res4=0;
template <typename Function>
void do_loop_ref(Function & func, const uint64_t upper_limit = 100000)
{
for (uint64_t i = 0; i < upper_limit; ++i)
func(i);
}
template <typename Function>
void do_loop_forward(Function && func, const uint64_t upper_limit = 100000)
{
Function f(std::forward<Function>(func));
for (uint64_t i = 0; i < upper_limit; ++i)
f(i);
}
template <typename Function>
void do_loop_copy(Function func, const uint64_t upper_limit = 100000)
{
for (uint64_t i = 0; i < upper_limit; ++i)
func(i);
}
void test_bind_copy()
{
{
namespace arg = std::placeholders;
uint64_t x = 0;
auto accumulator = std::bind(&test_accumulate_bind_function, std::ref(x), arg::_1);
std::cout << "reference:";
timer t;
do_loop_ref(accumulator);
res1 = x;
}
{
namespace arg = std::placeholders;
uint64_t x = 0;
auto accumulator = std::bind(&test_accumulate_bind_function, std::ref(x), arg::_1);
std::cout << "copy:";
timer t;
do_loop_copy(accumulator);
res2 = x;
}
{
namespace arg = std::placeholders;
uint64_t x = 0;
auto accumulator = std::bind(&test_accumulate_bind_function, std::ref(x), arg::_1);
std::cout << "localcopy:";
timer t;
do_loop_forward(accumulator);
res3 = x;
}
{
namespace arg = std::placeholders;
uint64_t x = 0;
auto accumulator = std::bind(&test_accumulate_bind_function, std::ref(x), arg::_1);
std::cout << "move:";
timer t;
do_loop_forward(std::move(accumulator));
res4 = x;
}
printf("res1:%lld, res2:%lld, res3:%lld, res4:%lld\n", res1, res2, res3, res4);
}
void test_copy()
{
{
uint64_t x = 0;
auto accumulator = [&x](uint64_t i){ return x += i; };
std::cout << "reference:";
timer t;
do_loop_ref(accumulator);
res1 = x;
}
{
uint64_t x = 0;
auto accumulator = [&x](uint64_t i){ return x += i; };
std::cout << "copy:";
timer t;
do_loop_copy(accumulator);
res2 = x;
}
{
uint64_t x = 0;
auto accumulator = [&x](uint64_t i){ return x += i; };
std::cout << "localcopy:";
timer t;
do_loop_forward(accumulator);
res3 = x;
}
{
uint64_t x = 0;
auto accumulator = [&x](uint64_t i){ return x += i; };
std::cout << "move:";
timer t;
do_loop_forward(std::move(accumulator));
res4 = x;
}
printf("res1:%lld, res2:%lld, res3:%lld, res4:%lld\n", res1, res2, res3, res4);
}
int main()
{
test_copy();
test_bind_copy();
}
我的电脑上的(vs2013)输出为:
参考:196 副本:65 localcopy:196 移动:64
res1:4999950000,res2:4999950000,res3:4999950000,res4:4999950000
参考:359 复制:361 localcopy:358 移动:358
res1:4999950000,res2:4999950000,res3:4999950000,res4:4999950000
那么为什么在lambda调用中,传递值比引用快得多。 我也通过引用测试lambda捕获空字符串,输出如上所示;但是当按值捕获空字符串时,ref和复制成本时间将接近。
bashrc的答案提醒我,我添加了两个测试,结果很有意思,移动成本几乎与传递值相同,但如果复制成本最多的时候,为什么传递值比参考更快;答案 0 :(得分:0)
您是否运行了打开编译优化的基准测试?
在gcc中使用-O3
进行编译时,两个代码在汇编中看起来完全相同(因此应该执行完全相同的操作)。
没有任何优化标志,代码看起来仍然相同,只是当你通过引用传递时有一个额外的间接级别。
// pass by reference
mov rax, QWORD PTR [rbp-24]
// pass by value
lea rax, [rbp-32]
https://www.diffchecker.com/cCBmQuW7
std::function
内部只是包含函数内部状态的结构(在lambda的情况下捕获的变量)。通过引用传递function
可以想象为将指针传递给具有该函数捕获的所有状态的结构。因此,调用者和被调用者正在相同的内存位置上工作。当按值传递时,调用者和被调用者具有独立的副本,并且写入和读取一个不会影响另一个副本。
在上面的情况下,当函数按值传递时,地址按原样加载,而当它通过引用传递时,在调用lambda之前会有一个额外的内存读取。校验 What is the difference between MOV and LEA
额外的间接级别可能会带来诸如引用位置之类的内容,但这些内容将与执行此操作的硬件紧密耦合。但很大程度上取决于这个代码是如何编译的,执行的代码在哪里以及如何衡量(你还没有共享timer
的代码)。因此,在问题的有限信息的情况下,额外的阅读是我将额外的时间归咎于他们。