为什么编译影响lambda到std :: function的代码这么慢,特别是对于Clang?

pra*_*pin 7 c++ lambda clang c++11 std-function

我发现,将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;
}
Run Code Online (Sandbox Code Playgroud)

根据MODE预处理器宏,该代码可以在以下三种方式之间进行选择,以便通过lambda函数传递add函数:

  1. std::function<> 模板类
  2. 一个简单的C指针函数(这里可能只因为没有捕获)
  3. std::function由Malte Skarupke撰写的快速替代(https://probablydance.com/2013/01/13/a-faster-implementation-of-stdfunction/)

无论何种模式,程序始终以常规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
Run Code Online (Sandbox Code Playgroud)

Whow.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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我现在正在考虑使用Malte Skarupke的实现,既可以提高运行时性能,又可以提高编译时间.

小智 0

使用 --save-temps 选项查看编译器在每种情况下必须处理的内容。在我的使用 clang 6.0.1 的机器上,由于包含大量标准库头文件,MODE=1 会生成一个 575K 的预处理文件。MODE=1 生成一个 416字节的文件,小 1000 倍。生成的程序集也相差 10 倍。