-
Notifications
You must be signed in to change notification settings - Fork 3
/
cpp编码优化准则
150 lines (108 loc) · 7.15 KB
/
cpp编码优化准则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#
# *1.程序性能优化方法:
#
1.针对io瓶颈的性能优化:
每次io操作大概在10ms量级, 100次就需要1秒左右, 因此尽量避免不必要的io操作.具体做法如下:
(1)预先顺序读文件避免随机访问
(2)合并多个小文件为单个大文件
(3)优化动态库文件的加载
(4)交错io时间和CPU时间
2.针对计算密集的性优化:
计算密集的性能问题主要有内存分配性能,字符串操作,共享变量的互斥锁保护等, 具体优化方法如下:
(1)去除冗余代码
(2)字符串操作优化
(3)减少内存的创建&释放, 合理使用内存池, 提高内存重用率
(4)减少不必要的互斥锁操作
(5)根据性能需求, 选择合适的数据结构容器
(6)延迟工作, 按需执行(合理使用io异步)
(7)减少跨进程的调用/多线程资源抢夺
(8)尽量使用strncpy, strnlen, snprintf 等函数, 构建高性能'线程安全函数'
3.c++语言特性相关的性能优化, 包括:
* 构造, 析构函数的逻辑, 需要严格遵守'线程安全函数'原则
* 虚函数, 节约逻辑代码段的数量
* 内联函数, 对于精简函数, 极大地提高这种函数的性能
* 引用, 可以优化函数参数之间的传递效率
* 善用编译器内置函数宏(这方面clang 编译器比较丰富)
* 善用编译优化选项
4.用户体验的性能优化:
有些设计不能真正提升性能, 但能让用户体验到了性能提升, 如:
(1)流式播放设计, 用户不需要等到视频文件下载完成后, 再播放, 可以边下载边播放.
(2)线程化设计, 对于需要较长时间完成的操作, 可以设计为非阻塞式的, 用户可以在等待时间完成其它操作任务.
5.设计层面的性能优化:
这个需要根据业务需求和软件整体架构, 具体问题具体分析.
#
# *2.c 基础优化
#
const常量优化, 用处: 节约内存, 锁定变量'类型/值';
static静态变量优化, 用处: 节约内存, 保存执行结果, 单例等;
#define宏定义优化, 用处: 编译器替换, 最大限度优化程序; (缺点: 宏不能二次展开, 宏不能做复杂逻辑, 只是简单的字符串替换, 用途有限);
local函数压栈,出栈优化, 用处: 减少函数形参, 减少返回值体积, 尽量用指针代替, 可以减少线程栈的压栈, 出栈操作, 节约时间, 提高性能;
local变量定义前置, 用处: 迎合cpu 偷步, 预取指令的设计, 防止编译器优化造型逻辑错误;
削减local变量的体积, 用处: 减少线程栈的压栈, 出栈操作, 节约时间, 提高性能(可以用指针代替);
io瓶颈的性能优化, 用处: 减少io操作造成的时间等待, 提高cpu利用率(参考上面);
使用assert()代替if 用处: assert()可以在后期屏蔽掉, 可以减少if的使用, 减少预测分支的产生;
减少if,for,while的使用 用处: 减少预测分支的产生(尽量少用, 但申请资源, 必须if 资源是否申请到位, 资源申请到位之后, 初始化资源一般不需要再用if);
减少线程锁的使用 用处: 线程锁其实和if 一样, 都是不可预测分支, 会拖慢cpu 预取的性能;
尽量内存对齐 用处: 提高内存分配, 释放, 操作的效率(虽然会有些余料损耗, 但问题不大);
减少指针的使用 用处: 提高代码的可靠性, 减少编码难度, 减少阅读难度;
善用指针的引用 用处: 减少线程栈的压栈, 出栈操作, 节约时间, 提高性能;
善用缓冲区截断 用处: 减少buf 缓冲区溢出bug, 如: strncpy();
善用线程安全的API 用处: 减少glibc 中, 部分非线程安全的函数, 导致多线程访问static 造成的错误, 如: random_r();
善用连续内存拷贝优化 用处: 使用memset,memcpy 对连续内存如一维数组,二维数组,三维数组,n维数组初始化or拷贝, 可以提高一点效率;
尽量多使用原子操作 用处: 减少编码语句, 减少if的使用, 减少资源重复操作的可能性;
其他优化:
尽量在初始化中, 多做工作,
减少运行过程中做初始化操作, 减少后续初始化带来的顿卡问题,
原则上, 运行过程中只跑逻辑.
#
# *3.cpp 基础优化
#
善用inline 内联函数 用处: 减少线程栈的压栈, 出栈操作(缺点:exe程序较大,内存消耗较多)
[ps: inline 比宏好用, 方便, 但有性能消耗, 性能上没有宏好]
宏只是编译器, 预处理时的字符串替换, 作用并没有想象中的大,
宏只是程序员与编译器沟通的渠道而已, 并没有那么神奇.
提高类封装的隐蔽性 用处: 减少编码时, 选择api 时的思考负担;
public 最好只提供API, private 尽量多的存放内部变量;
善用virtual 虚函数 用处: 节约函数逻辑消耗的内存, 让类实体尽量只为local变量数据而消耗内存;
[ps: 多线程访问虚函数内的static, 全局变量时, 需要加锁]
(c/c++普通函数, 是全局变量, 如果函数实体是唯一的,
则以'只读'访问函数内的static, 全局变量时, 可以不加锁, 以'写'的方式访问, 同样需要加锁)
善用gun stl 用处: 不要像c 一样自己实现容器, 节约时间, 提高可靠性, 减少出错的可能, 站在巨人的肩膀上;
善用c++多态特性 用处: 模板类, 函数重载, 可以极大地减少代码量, 提高可读性;
善用类集成特性 用处: 提高代码复用率, 减少代码量, 提高可读性;
善用构造函数初始化数据 用处: c++ 基础原则
善用析构函数释放数据 用处: c++ 基础原则
善用namespace 用处: 以人名来划分区域, 方便团队合作; 以库名来划分区域, 方便划分不同编码库;
其他优化:
指针数量在10W-200W之间, 用智能指针优化程序
#
# *4.内存重用优化
#
善用gnu stl空间管理器 用处: 逻辑上提高变量实体的内存重用(gun stl也有几种空间管理器可供选择)
善用ptmalloc 用处: glibc进程堆内存重用器, 每个进程自带一个, 系统级重用;
善用jemalloc 用处: 第三方进程堆内存重用器, 每个进程自带一个, 系统级重用;
善用tcmalloc 用处: 第三方进程堆内存重用器, 每个进程自带一个, 系统级重用;
#
# *5.多线程/多进程同步优化
#
'多线程/多进程'无锁设计 用处: 性能最好
一次加锁,批量操作设计 用处: 折中选择(可靠性高)
减少'共有数据'的数量, 才是最终的'多线程/多进程'同步优化方法;(减少同步'读写访问'的次数, 减少每次访问的时长)
#
# *6.io优化
#
参考上面
#
# *7.善用shell 减少c++ 编码压力
#
'使用shell分担cpp编码压力'的重要意义:
简单即最稳定, 最可靠, 最安全;
c/c++ 都是高性能计算程序, 只适合做高速运算, 不适合做一些复杂, 繁琐的工作;
因此, 把不需要高速运算的模块, 全部交给:
shell
lua
python
java
把一些功能性的任务, 分配给这些脚本机来做,
不但可以提高程序的可靠性, 而且可以减少开发压力, 降低后期代码运维成本,
这些功能用c++ 来做的话, 十分复杂, 用脚本来实现, 反倒很简单;