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中只使用对应类型的指针,如下所示:

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

文章插图
在编译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文件指定的编译依赖关系在一定程度上限制了编译并行度的执行 。
比如下面这个场景,通过合理设置库文件依赖关系,可以提高编译并行度 。
推荐阅读
- 聊聊CDN与高性能流媒体服务器的关键技术设计
- Windows服务器及PC漏洞补丁批量自动升级-WSUS
- 如何实现HTTPS服务器
- 服务业结构 服务业管理类
- 深圳瑞吉酒店服务怎么样
- 秒级搭建MySQL数据库服务,太香了
- 盘点c++几种常见的设计模式及具体实现
- CentOS7下安装Emby流媒体服务器
- 从零开始入门K8S| 从Spring Cloud到Kubernetes的微服务迁移实践
- 如何为你的网站构建无服务器NLP聊天机器人
