【读后感】编程珠玑 第九章 代码调优

  引子: 有些程序员过于关注程序的效率;由于太在乎细小的“优化”,他们编写出的程序过于竞标,难以维护。而另外一下程序员很少关注程序的效率;他们编写的程序有着清晰漂亮的结构,但效率极低以至于毫无用处。优秀的程序员将程序的效率难入整体考虑之中:效率只是软件中的众多问题之一,但有时候也很重要。 

 9.1 典型的故事 

 问题中到了Van Wyk将一个3000行的程序的运行时间减少了一半。Van做的首先是监视程序的性能,以确定每个函数需要花费的时间,他发现将近70%的运行时间都用在了内存分配函数malloc上,于是他决定利用高速缓存来避免使用malloc。 最终,他成功了。 

 

 9.2 急救方案集锦 

 作者写出了一些比较实用的小技巧,个人感觉还是挺有用的。 

1. 整数取模 

 mod还是比较花时间的,有时候可以考虑使用减法。例如 mod n, 有时可以使用 -n来代替。 

 “在过去,程序员知道,如果程序的运行时间主要消耗在输入输出上,那么对程序中的计算进行加速是毫无意义的。在现代的体系结构中,如果对内存的访问占用了大量的运行时间,那么减少计算时间同样是毫无意义的。” 

2.函数、宏、内联代码 

 我已经看到很多次不提倡用宏(#define)了,理由有很多,宏只是字符串替换,不能进行类型检查,有时还能会影响程序效率。 

 习题中举出了一个例子,arrmax(int n)是找出数组中的最大元素,代码如下:

 

 float arrmax(int n) 

 { 

     if(n == 1) return x[0];

     else return max(x[n-1],arrmax(n-1)); 

 } 

 #define max(a,b) ((a) > (b) ? (a) : (b)) 

 

 程序看起来没有错误,但是由于max中的b是一个函数的返回值,假如每次比较都是b大(函数返回值),那么判断a > b会计算一次arrmax,返回结果时又会计算一次arrmax。这样就会重复很多很多次(2的指数级增长),如果换成max函数就不会。我自己做了一次试验,发现用max宏运算时间是3秒,用max函数0秒(估计是小于1秒) 

3.顺序搜索 

 在数组中遍历搜索时,for循环中会判断两次,一次是 i < n,一次是 x[i] == t。作者使用哨兵技术就避免了判断 i < n 

 

 int search(t) 

     hold = x[n] 

     x[n] = t 

     for (i = 0 ; ; i++) 

     {

         if x[i] == t  break 

         x[n] = hold 

         if i == n return -1 //表示没有找到 

         else return i 

     }

 

 作者最后展示了一种更快的算法,不过我不推荐。有兴趣的同学可以自己下来看看。 

4.计算球面距离 

 在计算地球表面两个点时,需要使用非常复杂的计算方法,计算时占用了大量的时间。

“Andrew Appel发现了一个关键点:为什么一定要在数据结构的层面解决这个问题呢?为什么不使用简单的数据结构,将这些点保存在一个数组中,通过调优代码来降低个点之间距离的计算开销”

 

 9.3 大手术——二分搜索 

 作者在这一节主要对二分搜索进行了代码调优,共给出了3个优化的二分搜索的代码。但是我觉得除了第1个之外,剩下的两个都有一些特殊的技巧在里面,给人一点华而不实的感觉。

现在给出代码 

原始二分查找: 

 

l = 0 u = n-1 

loop 

    if l > u p = -1 break; 

    m = (l+u) / 2 

    case x[m] < t : l = m + 1 

         x[m] == t : p = m; break; 

         x[m] > t : u = m - 1 



 优化的二分查找:

 

 

l = -1 

u = n 

while l + 1 != u  

{        

    m = (l + u) /2        

    if x[m] < t l = m         

    else u = m  

} 

p = u 

if p > n || x[p] != t  

p = -1 

 

优化过的代码只有一次判断,如果x[m] >=t 那么就 u = m。在程序的最后,使用最后的if来断定是找到t还是没有找到。但是由于程序中没有break,也就是说只有l+1 == u的时候循环才会退出,假如程序运行第一次就找到t,由于没有break,程序还是要坚持把while走完。 

 

 9.4 原理 

 作者提出了一些代码调优的原理。 效率的角色。 不成熟的优化是大量编程灾难的根源,当可能的危害影响较大时,请考虑适当将效率放一放。 度量工具。 当效率很重要时,我们要确定某些大量消耗时间的代码,然后进行修改。“我们遵循有名的格言是:没有坏的话就不要修”。 设计层面。效率问题可以有多种方法来解决,参考第六章。只有在确信没有更好的解决方案时才考虑进行代码调优。 双刃剑。在进行“改进”之后,用具有代表性的输入来度量程序的效果是至关重要的。“小心玩火自焚”。

你可能感兴趣的:(编程珠玑)