C++服务编译耗时优化原理及实践( 四 )


文章插图
 
目前,这个工具支持一键化生成编译耗时分析结果,其中几个小工具,比如依赖文件个数工具已经集成到公司的上线集成测试流程中,通过自动化工具检查代码改动对编译耗时的影响,工具的建设还在不断迭代优化中,后续会集成到公司的MCD平台中,可以自动分析来定位编译耗时长的问题,解决其它部门编译耗时问题 。
四、优化方案与实践通过运用上述相关工具,我们能够发现Top10编译耗时文件的共性,比如都依赖消息总线文件platform_query_analysis_enent.h,这个文件又直接间接引入2000多个头文件,我们重点优化了这类文件,通过工具的编译展开,找出了Boost使用、模板类展开、Thrift头文件展开等共性问题,并针对这些问题做专门的优化 。此外,我们也使用了一些业内通用的编译优化方案,并取得了不错的效果 。下面详细介绍我们采用的各种优化方案 。
4.1 通用编译加速方案
业内有不少通用编译加速工具(方案),无需侵入代码就能提高编译速度,非常值得尝试 。
1. 并行编译
在linux平台上一般使用GNU的Make工具进行编译,在执行make命令时可以加上-j参数增加编译并行度,如make -j 4将开启4个任务 。在实践中我们并不将该参数写死,而是通过$(nproc)方法动态获取编译机的CPU核数作为编译并发度,从而最大限度利用多核的性能优势 。
2. 分布式编译
使用分布式编译技术,比如利用Distcc和Dmucs构建大规模、分布式C++编译环境,Linux平台利用网络集群进行分布式编译,需要考虑网络时延与网络稳定性 。分布式编译适合规模较大的项目,比如单机编译需要数小时甚至数天 。DQU服务从代码规模以及单机编译时长来说,暂时还不需要使用分布式的方式来加速,具体细节可以参考Distcc官方文档说明 。
3. 预编译头文件
PCH(Precompiled Header),该方法预先将常用头文件的编译结果保存起来,这样编译器在处理对应的头文件引入时可以直接使用预先编译好的结果,从而加快整个编译流程 。PCH是业内十分常用的加速编译的方法,且大家反馈效果非常不错 。在我们的项目中,由于涉及到很多Shared Library的编译生成,而Shared Library相互之间无法共享PCH,因此没有取得预想效果 。
4. CCache
CCache(Compiler Cache)是一个编译缓存工具,其原理是将cpp的编译结果保存在文件缓存中,以后编译时若对应文件无变动可直接从缓存中获取编译结果 。需要注意的是,Make本身也有一定缓存功能,当目标文件已编译(且依赖无变化)时,若源文件时间戳无变化也不会再次编译;但CCache是按文件内容做的缓存,且同一机器的多个项目可以共享缓存,因此适用面更大 。
5. Module编译
如果你的项目是用C++ 20进行开发的,那么恭喜你,Module编译也是一个优化编译速度的方案,C++20之前的版本会把每一个cpp当做一个编译单元处理,会存在引入的头文件被多次解析编译的问题 。而Module的出现就是解决这一问题,Module不再需要头文件(只需要一个模块文件,不需要声明和实现两个文件),它会将你的(.ixx 或者 .cppm)模块实体直接编译,并自动生成一个二进制接口文件 。import和include预处理不同,编译好的模块下次import的时候不会重复编译,可以大幅度提高编译器的效率 。
6. 自动依赖分析
google也推出了开源的Include-What-You-Use工具(简称IWYU),基于Clang的C/C++工程冗余头文件检查工具 。IWYU依赖Clang编译套件,使用该工具可以扫描出文件依赖问题,同时该工具还提供脚本解决头文件依赖问题,我们尝试搭建了这套分析工具,这个工具也提供自动化头文件解决方案,但是由于我们的代码依赖比较复杂,有动态库、静态库、子仓库等,这个工具提供的优化功能不能直接使用,其它团队如果代码结构比较简单的话,可以考虑使用这个工具分析优化,会生成如下结果文件,指导哪些头文件需要删除 。
>>> Fixing #includes in '/opt/meituan/zhoulei/query_analysis/src/common/qa/record/brand_record.h'@@ -1,9 +1,10 @@ #ifndef _MTINTENTION_DATA_BRAND_RECORD_H_ #define _MTINTENTION_DATA_BRAND_RECORD_H_-#include "qa/data/record.h"-#include "qa/data/template_map.hpp"-#include "qa/data/template_vector.hpp"-#include <boost/serialization/version.hpp>+#include <boost/serialization/version.hpp>  // for BOOST_CLASS_VERSION+#include <string>                       // for string+#include <vector>                       // for vector++#include "qa/data/file_buffer.h"        // for REG_TEMPLATE_FILE_HANDLER


推荐阅读