37

这已经在为什么 C++ lambda 在多次调用时比普通函数慢?C++0x Lambda 开销 但我认为我的示例与前者的讨论有点不同,并且与后者的结果相矛盾。

在寻找代码中的瓶颈时,我发现了一个递归模板函数,它使用给定的处理器函数处理可变参数列表,例如将值复制到缓冲区中。

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 函数而无需支付运行时费用。

4

1 回答 1

56

这里的问题是您对 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' body 也可以内联,尤其是对于 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<> 。这样您至少可以避免复制,但不会内联调用。

以下是一些性能结果(使用 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
于 2013-09-04T17:03:24.317 回答