-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Description
问题描述 Please describe your issue
背景
Paddle 在近期的更新支援了 Ninja 在 Linux 环境中编译 ( #44210, #48435, #48932, #42283 ),在我们的硬体环境下表现十分出色(256核心,HDD 硬盘),不管是 cmake configure 还是编译的时间都远优于 Makefile. 如下表
| cmake 时间 | 编译时间 | 编辑 dense_tensor.h 后重编 paddle_python 时间 |
|
|---|---|---|---|
| Ninja (无 ccache) | 105 | 1794 | x |
| Ninja (有 ccache) | 103 | 924 | 1290 |
| Makefile (无 ccache) | 226 | 2475 | x |
| Makefile (有 ccache) | 223 | 1020 | 1839 |
在实验可以看出 Ninja 得益于更好的依赖判断来提高编译时的处理器利用率,并且 Ninja 不是透过 -M 生成的大量依赖档案,能减少 I/O 开销。在没有 ccache 或是更改头文件的时候能大幅的提高编译效率。
分析与优化建议
Ninja 编译完成后会产生 .ninja_log 的文件,可以透过 ninjatracing 转换成 chrome's about:tracing 的格式并且用 https://ui.perfetto.dev 视觉化。下图是在有 ccache 的情况下编译 GPU + 推理的时间分布图,对应的 cmake 参数见附录
从视觉化的图表可以看出有几处瓶颈值得优化
- 第三方库
protobuf - 第三方库
warpctc libphi_cpu,libphi_gpu的依赖导致顺序的执行 (此处应该可以平行化)libphi_static_1.alibpaddle_inference_c.so- .timestep (setup.py)
我简单看了一下这几处的瓶颈也提供一些建议可以改善编译所需的时间
1. protobuf
Paddle 在 protobuf 的 ExternalProject_Add 覆写了 CONFIGURE_COMMAND,导致 protobuf 没有启用 Ninja generator,因此变成串行编译,造成很严重的编译性能瓶颈。此处预期可以带来 2~3 分钟的编译时间减少。
2. warpctc
warpctc 的 CMakeLists.txt 用了已经被废弃的 CUDA_ADD_LIBRARY,不仅不能被 ccache 支援,也不能用上 cuda 11.2 的多架构平行编译功能,相关的改动我已经提了一个 PR 在 baidu-research/warp-ctc 底下。此处预期可以带来 1~2 分钟的编译时间减少。
3. libphi_gpu.a 和 libphi_cpu.a 相互依赖
图中标注 3 的区块中,棕色的是 libphi_gpu.a,深绿色的是 libphi_cpu.a。可以看到 libphi_cpu.a 没有跟 libphi_gpu.a 并行执行,反而是等 libphi_gpu.a 执行完后再执行,造成了约 30 秒的瓶颈。透过在 paddle/phi/CMakeLists.txt 添加以下指令后会看到 libphi_cpu.a 和 libphi_gpu.a 互相依赖,导致 Ninja 不能并行生成这两个静态函式库。我理解这两个函式库应该不需要互相依赖,若能解决这个问题就能带来 30 秒的编译时间减少
get_target_property(OUT phi_gpu LINK_LIBRARIES)
message(STATUS phi_gpu_dependency=${OUT})
4. libphi_static_1.a
这部分看代码应该是要把所有的 phi 的依赖打包成一个静态函式库供给后续需要 phi 的档案使用,因为 libphi_static_1.a 似乎没有要对外使用,只有编译时期要做 linking 而已,使用 create_dummy_static_lib(phi LIBS ${PHI_DEPS} LIMIT 100) 从分析图上来看带来了将近一分半的性能瓶颈。我想这边若只是要制作一个 target 来给后面需要 phi 的依赖使用的话,使用
add_library(phi INTERFACE)
target_link_libraries(phi INTERFACE ${PHI_DEPS})
也可以达到相同的功能,并且能省下 build 资料夹 6G 的空间,加上带来一分半的编译时间缩短。
5. libpaddle_inference_c.so
看代码这似乎是给 c 提供的 inference 动态函式库,这边因为依赖 libpaddle_inference.a 导致有约一分半的性能瓶颈,我想这边应该能够修改 cmake 让 libpaddle_inference_c.so 依赖 libpaddle_inference.a 的依赖,这样这两个函式库就能并行执行了。或是 libpaddle_inference.a 没有要暴露出去,本来就只是为了 libpaddle_inference_c.so 而生成的话,可以用跟第四点一样的方法,将 https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/fluid/inference/CMakeLists.txt#L79-L80 替换成
add_library(paddle_inference INTERFACE)
target_link_libraries(paddle_inference INTERFACE ${fluid_moduels} ${phi_modules}
${phi_kernels} ${STATIC_INFERENCE_API} ${utils_modules})
就能带来一分半的编译时间缩短
6. .timestamp
这部分看代码应该是最后用 setup.py 制作 paddle 的 whl 用的,这部分我还没有详细看,似乎有一个 PR 是在做这个优化的。这部分带来了约 45 秒的性能瓶颈
以上六个性能瓶颈若能全部解决预期能带来 6-10 分钟的编译时间缩短,这在 CI 工作上能带来十分明显的收益,除了减少 Paddle 开发者的开发周期,远期而言也能因为开发效率的提升而有助于 Paddle 生态的使用者。
附录
cmake 参数
cmake -Bbuild -S. \
-GNinja \
-DINFERENCE_DEMO_INSTALL_DIR=/workspace/paddle/data \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CUDA_FLAGS='-t0' \
-DCUDA_ARCH_NAME=Manual \
-DCUDA_ARCH_BIN="70 75 80 86 90" \
-DWITH_INCREMENTAL_COVERAGE=OFF \
-DWITH_INFERENCE_API_TEST=ON \
-DWITH_DISTRIBUTE=ON \
-DWITH_COVERAGE=OFF \
-DWITH_TENSORRT=ON \
-DWITH_TESTING=ON \
-DWITH_CONTRIB=ON \
-DWITH_ROCM=OFF \
-DWITH_RCCL=OFF \
-DWITH_STRIP=ON \
-DWITH_MKL=OFF \
-DWITH_AVX=OFF \
-DWITH_GPU=ON \
-DWITH_PYTHON=ON \
-DPY_VERSION=3.8 \
-Wno-dev
