软件优化工作有许多基础方法,这里介绍了4大块共22个小方法。
一、Data structures
1. packing and encoding
packing:在一个machine word(字节\dw)中存储多于一个的数据
encoding:用更少的字节存储相同的数据
例:int型的 1912 比字符串型的 1912 占据空间小、
2. augmentation
在数据结构里加一些信息让基础动作更简便
例:在一个链表后append另一个链表,存储一个指向尾结点的指针,让append更快。
3.precomputation
对一些要用到的数据进行预计算,类似于动态规划
例:事先计算杨辉三角,以计算二项式系数
4. caching
前面的计算结果不删掉,一遍后续的计算能用,有点类似简陋版的动态规划。
5.sparsity
将结构化的数据紧凑化。改变数据结构,避免存储与计算 0。
例:稀疏矩阵的存储:用一个二维数列存储每个元素的值与在列的位置,用一个一维数组存储每行的数量(存储起始位置,存储指针)。
例:将图简化。
二、Logic
1. constant folding and propagation
将所有的常数计算都放到编译阶段
2.common-subexpression elimination
变量计算可以简化,目的,更少的基本计算语句执行
例 a=b+c d=c+e f=a-d 变为 a=b+c d=c+e f=b-e
3.algebraic identities
同相同效果的但是计算量更小的运算符符号替代。
例:比较sqrt(a)<b ==== a<b^2,开根号的消耗比平方大
4.short-circuiting
达到条件时提早停止循环,例如冒泡排序中设置停止条件
5.ording tests
条件判断语句中,可能有多个分支,越常见的分支放到最前面(有点类似哈夫曼树)
6.creating a fast path
创建提早成功、退出条件,这些退出条件的计算比较简单
7.combining tests
深度大于1的条件判断树,将其combine成深度为1的条件判断树,用switch等
三、loops
1.hoisting
loop-invariant code motion
将循环中常见计算等每次相同的计算外提
2.sentinels
哨兵,如循环中的flag,作退出条件
3. loop unrolling
循环展开
例:一个循环,12 * 1一个语句 改成 4 * 3个语句
优点:循环中更少的跳转指针的数量,可以获得更多的编译优化
4.loop fusion
如果可以,将两个循环合并到一个循环中
5.eliminationg wasted iterations
规定合理的循环边界
例:
flag = 30;
for(int i=0;i<flag;i++){
if(i<20)
do something
}
把n改为20
四、functions
1.inlining
使用内联函数,让运行速度赶上宏指令
2. tail-recursion elimination
尾递归优化,原理是编译器会把尾递归变成一个循环,防止爆栈
https://zhuanlan.zhihu.com/p/36587160
3.coarsening recursion
在某些时段用更有效率的代码代替原有函数
例:归并函数 优化成 归并插入函数