预编译 C++ 头文件是否会减慢链接时间?

Jam*_*noa 8 c++ linker precompiled-headers build-time

我的 externalFiles 目录中有 100 个 .h 文件。我的源代码中大约有 10 个 .cpp 文件包含这些 .h 文件。

因此,我#include externalFiles/.*h从 .cpp 文件中删除了所有指令,并将它们写入通过 Cmake 包含的 pch.h 标头中target_precompile_headers(${PROJECT_NAME} PRIVATE pch.h)

通过 Cmake 使用预编译头检查了构建时间,而不是简单地将 pch.h 包含在我的 .cpp 文件中。我使用以下方法记录了构建时间: set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time")set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CMAKE_COMMAND} -E time")

使用预编译头构建时间

[1/2] 构建 CXX 对象 CMakeFiles/AlgoCoding.dir/main.cpp.obj

经过时间:11 秒。(时间),11.413 秒。(钟)

[2/2] 链接 CXX 可执行文件 AlgoCoding.exe

经过时间:54 秒。(时间),53.459 秒。(钟)

构建时间仅包括标头

[1/2] 构建 CXX 对象 CMakeFiles/AlgoCoding.dir/main.cpp.obj

经过时间:14秒。(时间),13.35 秒。(钟)

[2/2] 链接 CXX 可执行文件 AlgoCoding.exe

经过时间:28 秒。(时间),28.109 秒。(钟)

结论

从这些时间来看,使用预编译头似乎确实减少了修改后的 .cpp 文件(main.cpp)的 .obj 创建的构建时间,但它大大增加了链接时间。

每个人都是这样还是我在这里做错了什么?

编辑:1

实验 #2测试预编译头的构建时间。

我尝试将 spdlog 库(包含 100 个 .h 文件的仅标头库)包含到我的基本 Cmake 项目中(只是一个 main.cpp 将 1 行记录到文件中)。有两种方法可以做到这一点。

  1. 只需将 spdlog 文件夹复制到我的源代码中并在 main.cpp 中使用 #include 指令
  2. 使用预编译头文件(pch.h 中 spdlog 的 #include 指令和 pch.h 添加到项目中,使用target_precompile_headers(${PROJECT_NAME} PRIVATE pch.h)

构建时间结果

----方法一

[1/2] 构建 CXX 对象 CMakeFiles/test.dir/main.cpp.obj

经过时间:22 秒。(时间),21.904 秒。(钟)

[2/2] 链接 CXX 可执行文件 test.exe

经过时间:18秒。(时间),18.311 秒。(钟)

----方法二

[1/2] 构建 CXX 对象 CMakeFiles/test.dir/main.cpp.obj

经过时间:18秒。(时间),18.231 秒。(钟)

[2/2] 链接 CXX 可执行文件 test.exe

经过时间:22 秒。(时间),21.604 秒。(钟)

结论

在准系统项目中预编译外部头文件仍然提供与仅使用 #include 指令相同的总构建时间。使用预编译标头时,构建 .obj 文件所需时间的小幅减少将通过链接 CXX 可执行文件所需时间的增加来补偿。

在预编译外部 .h 文件时,我需要做一些不同的事情来减少构建时间吗?或者这是预期的行为?

Jam*_*noa 1

Clang+LLD 与 gcc+LD

正如 @HolyBlackCat 所建议的,我尝试使用clang + LLD linker进行相同的编译。对于普通的 #include 头文件和预编译头(约 100 个 .h 文件)来说,构建时间确实得到了改善:

-------方法1(普通#include)
[1/2] 构建CXX对象 CMakeFiles/test.dir/main.cpp.obj
花费时间:7秒。(时间),6.997 秒。(时钟)
[2/2] 链接 CXX 可执行文件 test.exe
所用时间:9 秒。(时间),9.493 秒。(钟)

-------方法2(预编译头)
[1/2] 构建CXX对象 CMakeFiles/test.dir/main.cpp.obj
花费时间:5秒。(时间),4.515 秒。(时钟)
[2/2] 链接 CXX 可执行文件 test.exe
所用时间:9 秒。(时间),9.076 秒。(钟)

从这些结果来看,Clang+LLD 的执行速度明显快于 gcc+LD/gold。此外,与 Clang+LLD 一起使用时,预编译头能够减少总体构建时间,因为它减少了 .obj 文件的构建时间,但能够实现类似的链接时间。(请参阅问题编辑 1,了解 gcc+默认链接器的构建时间)


缩放

此外,@Marco van de Voort 提到要检查使用预编译头缩放 .obj/.cpp 文件的数量如何影响构建时间。因此,对于方法 2(预编译头文件),我使用 3 个 .cpp 文件(Scaling 3)和 5 个 .cpp 文件(Scaling 5)进行编译。结果如下:

-----方法 2 缩放 3 .cpp
构建所有 3 个 CXX
所用时间:7 秒。(时间),6.767 秒。(时钟)
[4/4] 链接 CXX 可执行文件 test.exe
所用时间:27 秒。(时间),27.404 秒。(钟)

-----方法 2 缩放 5 .cpp
构建所有 5 个 CXX 对象
所用时间:11 秒。(时间),11.416 秒。(时钟)
[6/6] 链接 CXX 可执行文件 test.exe
所用时间:47 秒。(时间),46.942 秒。(钟)

我们可以看到链接的时间增加是线性的〜(9 *要链接到的#cpp文件)。对于方法 1,缩放会产生更差的结果。


继续前进

尽管与 Clang+LLD 一起使用时预编译头文件可以提供更好的构建时间,但最好使用方法 3。找到您尝试使用的仅头文件库的库版本 (.lib/.dll) 。该库是静态/动态链接的,这样构建时间要快得多。

方法3:在Cmake中使用spdlog库(通过vcpkg安装):

set(CMAKE_PREFIX_PATH "<PathToVcpkg>\\installed\\x64-mingw-static") # THIS HAS TO COME BEFORE THE PROJECT LINE
project(test)
find_package(spdlog REQUIRED);  
add_executable(${PROJECT_NAME} ${project-targets})  
target_link_libraries(${PROJECT_NAME} PRIVATE spdlog::spdlog )  
Run Code Online (Sandbox Code Playgroud)

与使用仅标头库(包含 PCH)相比,使用 .lib 库版本(包含 PCH)的构建时间显着缩短:

-----方法 3 缩放 1 .cpp
[1/2] 构建 CXX 对象 CMakeFiles/test.dir/main.cpp.obj
花费时间:2 s。(时间),2.073 秒。(时钟)[2/2] 链接 CXX 可执行文件 test.exe
所用时间:10 秒。(时间),10.294 秒。(钟)

-----方法 3 缩放 5 .cpp
[5/6] 构建 CXX 对象 CMakeFiles/test.dir/main2.cpp.obj
花费时间:2 s。(时间),2.244 秒。(时钟)[6/6] 链接 CXX 可执行文件 test.exe
所用时间:12 秒。(时间),12.024 秒。(钟)


结论

  • 使用 Clang+LLD 编译时,预编译仅标头库可以缩短构建时间。
  • 如果该库版本可用,则使用 (.lib/.dll) 库版本将是最快的方法。