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

4.2 代码优化方案与实践
1. 前置类型声明
通过分析头文件引用统计,我们发现项目中被引用最多的是总线类型Event,而该类型中又放置了各种业务需要的成员,示例如下:
#include “a.h”#include "b.h"class Event {// 业务A, B, C ...  A1 a1;  A2 a2;     // ...  B1 b1;  B2 b2;  // ...};这导致Event中包含了数量庞大的头文件,在头文件展开后,文件大小达到15M;而各种业务都会需要使用Event,自然会严重拖累编译性能 。
我们通过前置类型声明来解决这个问题,即不引入对应类型的头文件,只做前置声明,在Event中只使用对应类型的指针,如下所示:

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

文章插图
 
只有在真正使用对应成员变量时,才需要引入对应头文件;这样真正做到了按需引入头文件 。
2. 外部模板
由于模板被使用时才会实例化这一特性,相同的实例可以出现在多个文件对象中 。编译器要对每一处模板进行实例化,链接器还要移除重复的实例化代码 。当在广泛使用模板的项目中,编译器会产生大量的冗余代码,这会极大地增加编译时间和链接时间 。C++ 11新标准中可以通过外部模板来避免 。
C++服务编译耗时优化原理及实践

文章插图
 
在编译A.cpp的时候,实例化出一个 max(int)版本的函数 。
// B.cpp#include "util.h"extern template void max<int>(int); // 外部模板的声明void test2(){    max(2);}在编译B.cpp的时候,就不再生成 max(int)实例化代码,这样就节省了前面提到的实例化,编译以及链接的耗时了 。
3. 多态替换模板使用
我们的项目重度使用词典相关操作,如加载词典、解析词典、匹配词典(各种花式匹配),这些操作都是通过Template模板扩展支持各种不同类型的词典 。据统计,词典的类型超过150个,这也造成模板展开的代码量膨胀 。
template <class R>class Dict {public:  // 匹配key和condition,赋值给record  bool match(const string &key, const string &condition, R &record);  // 对每种类型的Record都会展开一次private:  map<string, R> dict;};幸运的是,我们词典的绝大部分操作都可以抽象出几类接口,因此可以只实现针对基类的操作:
class Record {  // 基类public:  virtual bool match(const string &condition);  // 派生类需实现};class Dict {public:  shared_ptr<Record> match(const string &key, const string &condition);  // 使用方传入派生类的指针即可private:  map<string, shared_ptr<Record>> dict;};通过继承和多态,我们有效避免了大量的模板展开 。需要注意的是,使用指针作为Map的Value会增加内存分配的压力,推荐使用Tcmalloc或Jemalloc替换默认的Ptmalloc优化内存分配 。
4. 替换Boost库
Boost是一个广泛使用的基础库,涵盖了大量常用函数,十分方便、好用,然而也存在一些不足之处 。一个显著缺点是其实现采用了hpp的形式,即声明和实现均放在头文件中,这会造成预编译展开后十分巨大 。
// 字符串操作是常用功能,仅仅引入该头文件展开大小就超过4M#include <boost/algorithm/string.hpp>// 与此相对的,引入多个STL的头文件,展开后仅仅只有1M#include <vector>#include <map>// ...在我们项目中主要使用的Boost函数不超过二十个,部分可以在STL中找到替代,部分我们手动做了实现,使得项目从重度依赖Boost转变成绝大部分达到Boost-Free,大大降低了编译的负担 。
5. 预编译
代码中有一些平常改动比较少,但是对编译耗时产生一定的影响,比如Thrift生成的文件,模型库文件以及Common目录下的通用文件,我们采取提起预编译成动态库,减少后续文件的编译耗时,也解决了部分编译依赖 。
6. 解决编译依赖,提高编译并行度
在我们项目中有大量模块级别的动态库文件需要编译,cmake文件指定的编译依赖关系在一定程度上限制了编译并行度的执行 。
比如下面这个场景,通过合理设置库文件依赖关系,可以提高编译并行度 。


推荐阅读