【发布时间】:2019-03-01 03:12:10
【问题描述】:
我发现,将 lambda 函数转换为 std::function<> 值的相对少量代码的编译时间可能非常长,尤其是使用 Clang 编译器。
考虑以下创建 100 个 lambda 函数的虚拟代码:
#if MODE==1
#include <functional>
using LambdaType = std::function<int()>;
#elif MODE==2
using LambdaType = int(*)();
#elif MODE==3
#include "function.h" // https://github.com/skarupke/std_function
using LambdaType = func::function<int()>;
#endif
static int total=0;
void add(LambdaType lambda)
{
total += lambda();
}
int main(int argc, const char* argv[])
{
add([]{ return 1; });
add([]{ return 2; });
add([]{ return 3; });
// 96 more such lines...
add([]{ return 100; });
return total == 5050 ? 0 : 1;
}
根据 MODE 预处理器宏,该代码可以在以下三种方式之间进行选择,以将 lambda 函数传递给 add 函数:
-
std::function<>模板类 - 一个简单的 C 函数指针(这里可能只是因为没有捕获)
- 由 Malte Skarupke (https://probablydance.com/2013/01/13/a-faster-implementation-of-stdfunction/) 编写的
std::function的快速替换
无论何种模式,程序总是以常规的0 错误代码退出。
但是现在看看 Clang 的编译时间:
$ time clang++ -c -std=c++11 -DMODE=1 lambdas.cpp
real 0m8.162s
user 0m7.828s
sys 0m0.318s
$ time clang++ -c -std=c++11 -DMODE=2 lambdas.cpp
real 0m0.109s
user 0m0.056s
sys 0m0.046s
$ time clang++ -c -std=c++11 -DMODE=3 lambdas.cpp
real 0m0.870s
user 0m0.814s
sys 0m0.051s
$ clang++ --version
Apple LLVM version 10.0.0 (clang-1000.11.45.2)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
哇。 std::function 和指向函数模式的指针之间有 80 倍的编译时间差异! std::function 和它的替代品之间甚至相差 10 倍。
怎么可能?是否存在特定于 Clang 的性能问题,还是由于 std::function 要求的固有复杂性?
我尝试使用 GCC 5.4 和 Visual Studio 2015 编译相同的代码。编译时间也有很大差异,但没有那么大。
GCC:
$ time g++ -c -std=c++11 -DMODE=1 lambdas.cpp
real 0m1.179s
user 0m1.080s
sys 0m0.092s
$ time g++ -c -std=c++11 -DMODE=2 lambdas.cpp
real 0m0.136s
user 0m0.120s
sys 0m0.012s
$ time g++ -c -std=c++11 -DMODE=3 lambdas.cpp
real 0m1.994s
user 0m1.792s
sys 0m0.196s
Visual Studio:
C:\>ptime cl /c /DMODE=1 /EHsc /nologo lambdas.cpp
Execution time: 2.411 s
C:\>ptime cl /c /DMODE=2 /EHsc /nologo lambdas.cpp
Execution time: 0.270 s
C:\>ptime cl /c /DMODE=3 /EHsc /nologo lambdas.cpp
Execution time: 1.122 s
我现在正在考虑使用 Malte Skarupke 的实现,既要稍微提高运行时性能,又要大幅提高编译时间。
【问题讨论】:
-
你看过生成的程序集了吗?也许正在进行一些花哨的优化。
-
有趣的事实:使用
-O3gcc 将函数指针版本优化为 37 行汇编(在我的机器上)。我认为它可以在编译时计算结果 5050。作为比较:对于std::function,gcc 生成大约 8000 行汇编。使用std::function时似乎涉及很多复杂性,这可以解释编译时间。 -
@pschill 有趣的观察,GCC 在某些情况下确实可以做出很好的优化!我故意测量编译时间而不进行优化。不,我没有考虑查看生成的代码进行比较。
标签: c++ c++11 lambda clang std-function