1 前序
最近在项目中做了一次Linux服务端程序的性能优化,优化的目标是程序的业务层。因此没有涉及到网络、多线程等逻辑,优化点主要在c++语言层面。
2 C++语言层面的可优化点
2.1 标准库容器使用优化
C++提供了一整套方便我们使用的标准库容器,虽然方便了程序员编写代码,但是也增加了性能的消耗。通过Perf结果可以看到,业务层代码中使用了大量标准库容器,而性能的热点也往往集中在容器所提供的操作上。
2.1.1 vector
损耗点 | 分析 | 优化方案1 | 优化方案2 |
---|---|---|---|
push_back | push_back动作创建变量,附带申请内存,如果元素类型是大结构体,频繁的变量创建损耗性能;其次如果vector变量创建时没有设置好预留长度,push_back可能导致vector的扩容,消耗性能 | emplace_back | 以空间换时间,如果可以知道vector在实际业务中的最大长度,且不是很大,则可以使用固定数组来替代vector |
2.1.2 unordered_map
损耗点 | 分析 | 优化方案1 | 优化方案2 |
---|---|---|---|
find | 多字段的key在hash后的命中率比较低 | 自定义hash方法,将多字段整合为单字段 | 使用固定长度的数组替换unordered_map,通过数组下标的方式访问,替代查找 |
insert | 默认bucket数不足时,会导致bucket数扩容,这样会对所有记录重新排序,导致出现抖动 | 预设bucket数,根据实际的情况按照最大量设置 | - |
2.1.3 map
map是红黑树结构,如果有大量插入动作的话,换成unordered_map吧
2.2 减少内存申请和变量构建
内存申请和变量构建会消耗大量时间,宗旨就是以空间换时间,能提前申请好的就绝不在执行过程中申请和创建。
2.2.1 使用内存池
设计一个简单的memory_pool类,构造memory_pool对象时一次性申请足够的内存,由memory_pool对象维护。memory_pool对象要提供单条记录内存申请和回收机制,供外部使用。
2.2.2 消除临时变量
虽然临时变量一般都是在栈中申请和使用,但是也会消耗不小的性能。消除临时变量,把它变成类成员变量,在对象构造时就申请、创建好。
2.3 减少系统调用的次数
系统调用会消耗大量时间,通过逻辑优化,减少运行过程中不必要的系统调用。
编码中,常用到的系统调用如下:
- memcpy
- memset
- 文件读写(同步)
2.4 多线程锁
多线程竞争资源的访问、操作会大量消耗性能。肯定是轻易删不掉的,看看能不能通过结构优化、逻辑优化减少竞争资源的访问频度。
另外要注意隐藏的锁,比如C++11带来的原子操作(atomic)。某些原子操作自带锁,优化性能时可以关注一下。
2.5 逻辑优化
2.5.1 减少不必要的处理,简化逻辑
2.5.2 根据实际数据场景设计分支
根据数据场景的特点,预估哪种分支路径的可能性大,就把可能性大的分支处理放在前面,减少过多的无效判定。