lambda函数中按引用/值捕获的成本?

时间:2013-07-25 23:58:21

标签: c++ optimization c++11 lambda pass-by-reference

请考虑以下代码:

#include <iostream>
#include <algorithm>
#include <numeric>

int main()
{
    const unsigned int size = 1000;
    std::vector<int> v(size);
    unsigned int cst = size/2;
    std::iota(v.begin(), v.end(), 0);
    std::random_shuffle(v.begin(), v.end());
    std::cout<<std::find_if(v.begin(), v.end(), [&cst](const int& i){return i == cst;})-v.begin()<<std::endl;
    std::cout<<std::find_if(v.begin(), v.end(), [=](const int& i){return i == cst;})-v.begin()<<std::endl;
    return 0;
}

此代码使用值填充向量,对其进行混洗,然后搜索指定值的索引(这只是一个示例来说明我的问题)。可以通过引用或lambda函数中的值来捕获此值cst

我的问题:两个版本之间的性能是否存在差异,还是编译器会以相同的方式对它们进行优化?

通过引用传递常量基本类型和通过引用传递常量类(如在普通函数中)是一个很好的规则吗?

3 个答案:

答案 0 :(得分:6)

在实践中,小类型没有性能差异。

使用clang -O3我在两种情况下都得到相同的代码。没有优化clang生成不同的代码,复制版本恰好是一个较小的指令。

$ clang ref.cpp -O3 -std=c++11 -S -o ref.s
$ clang cpy.cpp -O3 -std=c++11 -S -o cpy.s
$ diff ref.s cpy.s

存在与const相关的小差异。

复制捕获为您提供const unsigned值。这将编译:

unsigned cst = 123;
[=](const int& i){ return i == ++cst; }

非const变量的引用捕获会导致非const unsigned&引用。这会将原始值修改为副作用:

unsigned cst = 123;
[&](const int& i){ return i == ++cst; }

作为良好规则应避免复制大对象。如果小对象在lambda范围内应该是常量,但在当前范围内不是常量,则copy-capture是一个不错的选择。如果lambda的生命周期超过了本地对象的生命周期,则复制捕获是唯一的选择。

答案 1 :(得分:2)

lambda捕获并不真正相关。区别在于:

int x = y;

for (...)
    if (x == z)
       ...

const int& x = y;

for (...)
    if (x == z)
       ...

也就是说,存储对const int的引用vs对int的copy.of。第一个版本永远不会慢,但我认为优化器将设法为两者生成相同的代码。编译两个版本,并反汇编以查看会发生什么。

答案 2 :(得分:1)

cstunsigned int因此不太可能有所作为。但是,如果您使用包含大量数据的大型类来执行此操作,则可能会有所不同,通过引用传递将更快。

在这种情况下要考虑的另一件事是在迭代向量时只复制一次对象。如果您看一下STL函数,大多数事情都是通过const引用或普通引用传递的,我不明白为什么捕获变量应该是任何不同的。虽然不幸的是你无法将变量捕获为const。

当然,在通过引用传递时总是要小心,因为你可以修改它,我认为在这种情况下,最好只传递一个const引用。

最后要考虑的是,由于编译器可能能够优化差异,您应该只使用您认为最佳指定意图的表单。所以基本上我同意你的假设,你应该

  

按值传递常量基本类型,按引用传递常量类