zoukankan      html  css  js  c++  java
  • 【读后感】编程珠玑 第九章 代码调优

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

     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 原理 

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

  • 相关阅读:
    docker镜像文件导入与导出,支持批量
    配置和启动Kubernetes服务
    在CentOS 7 上安装docker
    安装CentOS7精简版后的配置工作
    Docker镜像加速
    docker命令不需要敲sudo的方法
    建立时间和保持时间(setup time 和 hold time)
    时序收敛:基本概念
    GitHub: Windows 下的简单使用
    K-means算法和矢量量化
  • 原文地址:https://www.cnblogs.com/iammatthew/p/1818792.html
Copyright © 2011-2022 走看看