• CSAPP--优化程序性能


    一.编写高效的程序:

      1.选择合适的算法和数据结构。

      2.编写出编译器能够有效优化以转换为高效可执行的源代码。

      3.并行计算。当然重点还是第一个,良好的算法和数据结构大大减小了程序的时间复杂度。

    二.优化编译器的局限性:

      编译器可以对程序进行不同程序的优化,在终端中,编译时添加命令行选项-O1,-O2等等可以进行不同级别的优化,这样虽然提高了程序的性能,但是也加大了程序的规模。

      首先要进行安全的优化,在这个例子中:

    1  void twiddle1(int *xp,int *yp)
    2  {
    3     *xp+=*yp;
    4     *xp+=*yp         
    5  }
    6 void twiddle2(int *xp,int *yp)
    7  {
    8     *xp+=2**yp; 
    9  }

    编译器是不会将twiddle1优化成twiddle2的,虽然twiddle2只需要三次访问内存,而twiddle1需要6次。编译器需要考虑xp和yp相等的情况,这样两个函数的结果就会不同,两个指针可能只想同一个存储器位置的情况成为存储器别名使用第二个妨碍优化的因素是函数调用,f()+f()+f()+f()和4*f()总会有点区别的,因为执行一次f()有可能就修改了全局变量的值,在下次调用时环境就可能不同,属于函数的副作用

    三.理解现代处理器

      考虑利用处理器的微体结构的优化,完成一些基本的优化。

      1.整体操作(ICU,EU)。

      2.分支预测:现代处理器中采用了分支预测的技术,处理器在执行某条指令序列时会预测下一条指令的位置,是否选择分支,预测分支的目标地址,投机执行,如果预测错误,将状态重新设置到分支点的状态。

      3.功能单元的特性:延迟,运算所需要的总时间;发射时间,两个连续的同类型运算之间需要的最小时钟周期数。

      4.关键路径:这是执行一组机器指令所需要时钟周期数的一个下界,循环运算中,有些数据是不能同时并行运算的,他们必须一个接一个的运算,因为后一次运算依赖于前一次

    计算的结果。所以该计算流程就是该循环中的关节数据流。该数据流处理的必须用时,就成为了优化的界限。

    四.程序级的优化

      每元素的周期数,Cycles Per Element 来表示程序的性能。

      1.代码移动。比如将循环中计算结果不会改变的计算移出,减少计算。

      2.减少过程的调用。

      3.消除不必要的存储器引用。减少对存储器的访问,比如 *dest=*dest*a[i]中,每次循环都要进行一次存储器的访存。可通过先赋值给一个局部变量,最后再赋值给*dest。

      4.循环展开。通过增加每次迭代的计算的元素的数量,减少循环的迭代次数。展开的次数越高,CPE 性能越接近1。他减少了不直接有助于程序结果的操作的数量,比如循环索引的计算和条件分支,其次减少了整个计算中关键路径上的操作数量。

      5.提高并行性。多个积累变量通过多个变量计算最后再合并以提高程序性能。重新结合变换,对于一个计算表达式中,两个连乘,我们可以使用括号,让后一次乘法先进行,然后再进行前一次乘法。这样做的能提升程序速度的原理在于,如果使用顺序乘法,第一次乘法结果与第二次乘法结果都会保存在同一个寄存器中,无形中增长了关键路径。通过该优化方法,能使得关键路径变短。

      其实说这么多,最好的还是选择个合适数据结构和算法来实现程序的优化,对于大数据,可能需要的这些优化措施多一些。

  • 相关阅读:
    P3121 [USACO15FEB]审查(黄金)Censoring (Gold)
    P3389 【模板】高斯消元法
    P2260 [清华集训2012]模积和
    【Codevs1237&网络流24题】餐巾计划(费用流)
    【POJ3680】Intervals(费用流)
    【BZOJ1070】修车(费用流)
    【BZOJ1834】network 网络扩容(最大流,费用流)
    【POJ1149&BZOJ1280】PIGS(最大流)
    【BZOJ2127】happiness(最小割)
    【BZOJ3894】文理分科(最小割)
  • 原文地址:https://www.cnblogs.com/a1225234/p/5731616.html
Copyright © 2020-2023  润新知