理解C ++ 11中lambda函数的开销

时间:2013-09-04 16:19:24

标签: c++ performance lambda variadic-templates

Why C++ lambda is slower than ordinary function when called multiple times?C++0x Lambda overhead已触及此内容 但我认为我的例子与前者的讨论略有不同,与后者的结果相矛盾。

在我的代码中搜索瓶颈时,我找到了一个recusive模板函数,它处理具有给定处理器函数的可变参数列表,比如将值复制到缓冲区中。

template <typename T>
void ProcessArguments(std::function<void(const T &)> process)
{}

template <typename T, typename HEAD, typename ... TAIL>
void ProcessArguments(std::function<void(const T &)> process, const HEAD &head, const TAIL &... tail)
{
  process(head);
  ProcessArguments(process, tail...);
}

我将使用此代码的程序的运行时与lambda函数以及使用移动指针将参数复制到全局缓冲区的全局函数进行了比较:

int buffer[10];
int main(int argc, char **argv)
{
  int *p = buffer;

  for (unsigned long int i = 0; i < 10E6; ++i)
  {
    p = buffer;
    ProcessArguments<int>([&p](const int &v) { *p++ = v; }, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  }
}

使用g ++ 4.6和-O3编译,使用工具时间在我的机器上测量时间超过6秒

int buffer[10];
int *p = buffer;
void CopyIntoBuffer(const int &value)
{
  *p++ = value;
}

int main(int argc, char **argv)
{
  int *p = buffer;

  for (unsigned long int i = 0; i < 10E6; ++i)
  {
    p = buffer;
    ProcessArguments<int>(CopyIntoBuffer, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  }

  return 0;
}

需要大约1.4秒。

我不知道幕后发生的事情是什么解释了时间开销,我想知道我是否可以改变一些东西来使用lambda函数而不用运行时付费。

1 个答案:

答案 0 :(得分:38)

这里的问题是你对std :: function的使用。 您通过复制发送它,因此复制其内容(并在解除参数时递归执行)。

现在,对于指向函数的指针,内容就是指向函数的指针。 对于lambda,内容至少是指向您捕获的函数+引用的指针。这是复制的两倍。另外,由于std :: function的类型擦除复制任何数据很可能会更慢(不内联)。

这里有几个选项,最好的可能是传递不是std :: function,而是传递模板。好处是你的方法调用更有可能被内联,std :: function没有发生类型擦除,没有复制发生,一切都非常好。像那样:

template <typename TFunc>
void ProcessArguments(const TFunc& process)
{}

template <typename TFunc, typename HEAD, typename ... TAIL>
void ProcessArguments(const TFunc& process, const HEAD &head, const TAIL &... tail)
{
  process(head);
  ProcessArguments(process, tail...);
}

第二个选项也是这样,但是通过副本发送process。现在,复制确实发生了,但仍然整齐地内联。

同样重要的是process'身体也可以内联,特别是对于lamda。根据复制lambda对象及其大小的复杂性,通过复制传递可能比通过引用传递更快或更快。它可能更快,因为编译器可能比本地副本更难以推理引用。

template <typename TFunc>
void ProcessArguments(TFunc process)
{}

template <typename TFunc, typename HEAD, typename ... TAIL>
void ProcessArguments(TFunc process, const HEAD &head, const TAIL &... tail)
{
  process(head);
  ProcessArguments(process, tail...);
}

第三个选项是,尝试传递std :: function&lt;&gt;引用。这样你至少可以避免复制,但不会内联调用。

以下是一些性能结果(使用ideones的C ++ 11编译器)。 请注意,正如预期的那样,内联lambda正在为您提供最佳性能:

Original function:
0.483035s

Original lambda:
1.94531s


Function via template copy:
0.094748

### Lambda via template copy:
0.0264867s


Function via template reference:
0.0892594s

### Lambda via template reference:
0.0264201s


Function via std::function reference:
0.0891776s

Lambda via std::function reference:
0.09s