摘要
- 在Windows开发环境下,使用VSCode+CMake的C++项目,默认编译速度比较慢,找出原因并解决。
背景
- 公司项目是在Windows开发环境,使用Microsoft Visual Studio 2017集成开发环境,VC14编译器。而我更习惯使用VSCode,所以接收代码后还是在VSCode编辑器下进行日常开发,通过CMake构建项目。
- 但一直出现一个问题,无论是在CLion还是在Visual Studio 2017,项目编译速度都比在VSCode下快。
- 个人猜测,是某个编译开关,或者编译参数未设置。
未解决前
VSCode-CMake编译时CPU占用率:
VSCode-CMake编译耗时:
解决方法
- 从编译时输出日志上来看,默认构建方式为串行构建。串行构建是指在构建过程中,任务按顺序一个接一个地执行。每个任务必须在前一个任务完成后才能开始。
- 那解决方法就是开启并行构建。并行构建是指构建任务分解成多个子任务,并在多核或多处理器系统上同时执行这些任务。多个文件可以同时编译,编译完成后进行链接。
- 明确当前使用的编译器:cl.exe
- 增加并行编译选项: /MP
- 在CMake中配置:
1
2
3if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_compile_options(/MP)
endif()
解决后
VSCode-CMake解决后–编译时CPU占用率:
VSCode-CMake解决后–编译耗时:
总结
在 C++ 开发中,构建的效率对项目开发和部署至关重要。并行构建和串行构建是两种主要的构建方式,它们各有适用场景和优化方法。以下是详细解析:
1. 串行构建
定义
串行构建是指按照依赖关系顺序,一个编译任务接一个地运行,所有任务都在单一线程中依次执行。
特点
- 执行顺序:每次只编译一个文件或模块。
- 时间开销:时间复杂度较高,尤其是大型项目中,编译时间可能会很长。
- 资源利用:通常只使用单一 CPU 核心,其他核心处于空闲状态。
- 稳定性:依赖明确,不会出现任务调度或资源竞争问题。
适用场景
- 小型项目:代码文件少、依赖简单。
- 调试依赖问题:确保编译按顺序执行,有助于分析构建流程中的错误。
- 硬件限制:在单核 CPU 或资源受限环境下运行。
2. 并行构建
定义
并行构建是指通过多线程或多进程方式,同时编译多个文件或模块,从而提高构建效率。
特点
- 执行顺序:任务调度器根据依赖关系,尽可能同时运行多个任务。
- 时间开销:显著减少构建时间,尤其在多核 CPU 上表现更佳。
- 资源利用:充分利用多核 CPU 和内存带宽,提升构建效率。
- 复杂性:需要解决任务调度和资源争用的问题,可能引入构建冲突。
适用场景
- 大型项目:模块化良好,文件数目多,编译时间长。
- 多核系统:现代多核 CPU 可以显著加速并行编译。
- 自动化构建:如 CI/CD 系统,要求快速完成构建任务。
3. 并行构建的实现方式
在 MSVC(cl.exe
)中
使用
/MP
参数:
启用并行编译,具体线程数根据 CPU 核心数自动调整。1
cl.exe /MP source.cpp
设置线程数:
通过/MPn
指定最大线程数,例如:1
cl.exe /MP8 source.cpp
在 CMake 中配置:
1
2
3if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_compile_options(/MP)
endif()
在 GCC 和 Clang 中
使用
-j
参数:
配合make
或ninja
构建系统,指定并行任务数量。1
make -j8
自动选择线程数:
不指定具体数量时,通常根据 CPU 核心数自动调整。1
make -j
在 CMake 中配置:
生成 Ninja 构建文件(支持并行):1
2cmake -G Ninja .
cmake --build . -j8
在 MSBuild 中
使用
/m
参数:
启用多线程构建。1
msbuild MySolution.sln /m
指定线程数:
设置具体线程数量:1
msbuild MySolution.sln /m:8
在 Xcode 中
并行构建开关:
默认情况下,Xcode 使用多线程进行构建。调整并行任务数:
可通过xcodebuild
配置-parallelizeTargets
。
4. 并行构建的优势与挑战
优势
- 显著加速:在多核环境下,构建速度可以提升数倍。
- 资源高效:充分利用 CPU 和内存资源,降低硬件浪费。
- 现代硬件支持:适配当前主流硬件性能。
挑战
- 依赖关系管理:需要正确分析模块间的依赖,避免并行任务冲突。
- 资源竞争:过多的线程可能导致内存不足或 I/O 瓶颈。
- 调试难度增加:并行构建中的错误可能更难定位。
5. 并行和串行的对比
特性 | 串行构建 | 并行构建 |
---|---|---|
构建速度 | 慢 | 快 |
资源利用 | 低 | 高 |
依赖处理 | 简单 | 复杂 |
硬件需求 | 适合单核系统 | 适合多核系统 |
调试 | 简单 | 较难 |
适用场景 | 小型项目、简单依赖 | 大型项目、复杂依赖 |
6. 性能优化建议
- 增量构建:避免每次全量构建,只构建发生变更的模块。
- 依赖优化:减少模块间的循环依赖。
- 使用高效工具链:如
Ninja
,其调度机制比Make
更高效。 - 硬件优化:在 SSD 上构建或提升 CPU 核心数。
通过合理使用并行构建和优化工具链,可以显著提升 C++ 项目的开发效率。