1.内联:内联就是用被调用函数的实际代码取代这个函数的调用。许多老的编译器可以自动进行较小函数的内联,一些新的支持C++或C99的编译器支持语言中的关键字inline。有些热衷于内联的编译器甚至可以进行递归内联。
2.展开循环:当迭代和循环本身所耗费的时间相对于循环中指令所用的时间而言比较大时,可以用展开循环的方式释放系统。重复执行循环体几次,并且插入所需的最少代码以调整循环计数器和循环的其他条件。
见以下示例。用第二种方法,i<100的检测和回到循环头部的分支操作只执行11次而不是101次。





















3.合并循环:合并循环就是合并在同一变量的同一范围内的相邻循环。
4.循环倒置:有些机器有一个特殊的指令用来递减并与0比较。将循环改为向下计数(代替向上计数)并且在0时终止循环可以告诉编译器启用这些特殊的指令——如果有的话。
5.强度消减:强度消减就是用生成同样结果的不同表达式代替原有表达式以使计算更轻松(许多编译器可以自动执行这项功能)。例如乘以4就可以用左移2来代替。
6.循环恒定计算:如果没有副作用,计算中任何不依赖循环变量的部分都可以移出循环体。许多编译器可以自己执行这项功能,但他们总是把计算留在循环体内并等待程序员自己动手移出这些循环恒定计算(在某些情况下,我们知道值是不变的,但编译器要确保没有副作用)。
7.表达式分组。
8.尾递归消去
9.查表法:有时要考虑采用查表法,特别是当处理迭代或递归运算——如收敛级数或阶乘时。耗费恒定时间的计算往往比内存中进行检索的计算要快,因此此时采用查表法运行速度不会有所改进。
10.字符串处理:在C语言中大部分处理字符和字符串的库函数运行时间与字符串的长度成正比。
a.避免在包含字符串本身的循环中调用strlen。
b.在采用strcat把大字符串装入内存时,它会在每次调用时扫描字符串的整个长度。如果我们一直在跟踪字符串的长度,我们可以直接指向字符串的尾部,然后用strcpy函数或memcpy函数。
c.空字符串检测用*s=='/0'来代替strlen(s)==0的调用。
d.采用*s=='/0'代替strcpy(s,""),这样也会省去不必要的函数调用。
e.strcpy会用0来填充字符串。除了第一个用来终止字符串的0外,其他的0一般没有用处。
f.memcpy比memmove运行得快,因为它可以假定它的参数没有重叠。
11.变量:避免在最紧凑的循环中引用全局或静态变量。
12.使用浮点数:浮点数操作已经变得和整型数操作一样快了。一直采用浮点数表达式和变量,直到计算后期才与整型表达式和变量混合。
13.访问的局部性:优化内存访问时,首先应当考虑访问的局部性。
14.以行为主的寻址:在处理多维数组时,要确保首先增长最右边的下标。
现代编译器至少会执行下列优化:内联;循环展开;循环倒置;强度消减;循环恒定代码转移;寄存器分配。
转自:《标准C语言使用全书》