本章节后面所有的规则和建议,都应在不影响前述可读性等质量属性的前提下实施。
不能一味地追求代码效率,而对软件的正确、简洁、可维护性、可靠性及可测性造成影响。 产品代码中经常有如下代码:
int foo()
{
if (异常条件)
{
异常处理;
return ERR_CODE_1;
}
if (异常条件)
{
异常处理;
return ERR_CODE_2;
}
正常处理;
return SUCCESS;
}
这样的代码看起来很清晰,而且也避免了大量的 if else 嵌套。但是从性能的角度来看,应该把执行概率较大的分支放在前面处理,由于正常情况下的执行概率更大,若首先考虑性能,应如下书写:
int foo()
{
if (满足条件)
{
正常处理;
return SUCCESS;
}
else if (概率比较大的异常条件)
{
异常处理;
return ERR_CODE_1;
}
else
{
异常处理;
return ERR_CODE_2;
}
}
除非证明 foo 函数是性能瓶颈,否则按照本规则,应优先选用前面一种写法。
以性能为名,使设计或代码更加复杂,从而导致可读性更差,但是并没有经过验证的性能要求(比如实际的度量数据和目标的比较结果)作为正当理由,本质上对程序没有真正的好处。无法度量的优化行为其实根本不能使程序运行得更快。
记住:让一个正确的程序更快速,比让一个足够快的程序正确,要容易得太多。大多数时候,不要把注意力集中在如何使代码更快上,应首先关注让代码尽可能地清晰易读和更可靠。
将循环中与循环无关,不是每次循环都要做的操作,移到循环外部执行。
示例一:
for (int i = 0; i < 10; i++ )
{
sum += i;
back_sum = sum;
}
对于此 for 循环来说语句“back_Sum = sum;” 没必要每次都执行,只需要执行一次即可,因此可以改为:
for (int i = 0; i < 10; i++ )
{
sum += i;
}
back_sum = sum;
示例二:
for (\_UL i = 0; i < func_calc_max(); i++)
{
//process;
}
函数 func_calc_max()没必要每次都执行,只需要执行一次即可,因此可以改为:
\_UL max = func_calc_max();
for (\_UL i = 0; i < max; i++)
{
//process;
}
示例:多维数组在内存中是从最后一维开始逐维展开连续存储的。下面这个对二维数组访问是以 SIZE_B 为步长跳跃访问,到尾部后再从头(第二个成员)开始,依此类推。局部性比较差,当步长较大时,可能造成 cache 不命中,反复从内存加载数据到 cache。应该把 i 和 j 交换。
...
for (int i = 0; i < SIZE_B; i++)
{
for (int j = 0; j < SIZE_A; j++)
{
sum += x[j][i];
}
}
...
上面这段代码,在 SIZE_B 数值较大时,效率可能会比下面的代码低:
...
for (int i = 0; i < SIZE_B; i++)
{
for (int j = 0; j < SIZE_A; j++)
{
sum += x[i][j];
}
}
...
例如,使用线程池机制,避免线程频繁创建、销毁的系统调用;使用内存池,对于频繁申请、释放的小块内存,一次性申请一个大块的内存,当系统申请内存时,从内存池获取小块内存,使用完毕再释放到内存池中,避免内存申请释放的频繁系统调用.
如果编译器支持 inline,可以采用 inline 函数。否则可以采用宏。
在做这种优化的时候一定要注意下面 inline 函数的优点:其一编译时不用展开,代码 SIZE 小。其二可以加断点,易于定位问题,例如对于引用计数加减的时候。其三函数编译时,编译器会做语法检查。三思而后行。