gpt4 book ai didi

c++ - 了解 C++11 中 lambda 函数的开销

转载 作者:IT老高 更新时间:2023-10-28 23:27:47 26 4
gpt4 key购买 nike

这已经在 Why C++ lambda is slower than ordinary function when called multiple times? 中被提及和 C++0x Lambda overhead但我认为我的例子与前者的讨论有些不同,与后者的结果相矛盾。

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

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

最佳答案

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

以下是一些性能结果(使用 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

关于c++ - 了解 C++11 中 lambda 函数的开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18619035/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com