zoukankan      html  css  js  c++  java
  • CSAPP-程序优化

    代码移动:

    如果一个表达式总是得到同样的结果,最好把它移动到循环外面,这样只需要计算一次。编译器有时候可以自动完成,比如说使用 -O1 优化。一个例子:

    void set_row(double *a, double *b, long i, long n){
      long j;
      for (j = 0; j < n; j++){
       a[n*i + j] = b[j];
      }
    }
    

    这里n*i是重复被计算的,可以放到循环外面

    long j;
    int ni = n * i;
    for (j = 0; j < n; j++){
       a[ni + j] = b[j];
    }
    

    消除不必要的内存引用:

    for(int i = 0;i < n;i++)
    {
       *p = *p OP data[i];
    }
    
    //new
    int t = *p;
    for()
    {
       t = t OP data[i];
    }
    *p = t;
    

    上面的过程中,每次循环要经历2次读1次写:
    从p读取值 -> 读取data[i]进行计算 -> 写到p
    但该进之后,每次循环只需要一次读

    vmovesd (%rbx),%xmm();        //read from p
    vmulsd (%rdx),%xmm(),%xmm();  //mul by data[]
    vmovesd %xmm(),(%rbx);        //store
    
    vmulsd (%rdx),%xmm(),%xmm();  //mul by data[]
    

    处理条件分支

    现代处理器普遍采用超标量设计,也就是基于流水线来进行指令的处理,也就是说,当执行当前指令时,接下来要执行的几条指令已经进入流水线的处理流程了。

    对于顺序执行来说,不会有任何问题,但是对于条件分支来说,在跳转指令时可能会改变程序的走向,也就是说,之前载入的指令可能是无效的。这个时候就只能清空流水线,然后重新进行载入。为了减少清空流水线所带来的性能损失,处理器内部会采用称分支预测』的技术。

    例:
    当遇到if语句的时候,你事先完全不知道应该往哪边走。你可以暂停,等待直到之前的指令执行完成,然后比较结果,然后往正确的那个方向走。
    但是现代计算机有很长的流水线,等待结果必定会浪费很多时间。于是有了分支预测,先假设分支方向,加入流水线,如果对了就可以继续执行下去。否则清空流水线再向正确的地方前进。

    条件转移:

    在有的情况下,书写合适的条件转移可以实现分支。

    if(a > b)
    {
       int t = a;
       a = b;
       b = a;
    }
    

    上面的代码会进行分支预测,改:

    if(a > b)
    {
       int tmax = a < b? b:a;
       int tmin = a > b? b:a;
       a = tmin;
       b = tmax;
    }
    // a < b ? b:a;可以等效于:
    tmax = b;
    tmp = a;
    t = a < b;
    if(!t) tmax=tmp;
    

    循环展开:

    通过增加每次迭代的计算量,减少循环次数。
    1.减少了循环索引计数和条件分支
    2.提供了进一步利用机器特性进行的优化的机会

    int ans = 0;
    for(int i = 0;i < n;i += 2)
    {
       ans = ans OP data[i] OP data[i+1];
    }
    

    提高并行化:

    1.多个累积变量
    在加法和乘法功能的时候,功能单元是完全流水线化的。使其能更高效的执行

    for(int i = 0;i < n;i+=2)
    {
       ans0 = ans0 OP data[i];
       ans1 = ans1 OP data[i+1];
    }
    

    与上面相比关键路径上面的操作由 n->n/2。而且结果ans0 和 ans1不相关,流水线性能应该会提升。

    2.重新结合变化

     ans = ans OP data[i] OP data[i+1];
     ans = ans OP (data[i] OP data[i+1]);
    

    可能看着没什么区别,但是data[i]*data[i+1]的计算完全不用考虑上一次循环的结果,流水线能得到充分利用


    参考资料:

    不周山之读薄CSAPP:http://wdxtub.com/2016/04/16/thin-csapp-0/
    浅谈分支预测、流水线和条件转移:http://www.cnblogs.com/yangecnu/p/4196026.html#undefined
    《深入理解计算机系统 》

  • 相关阅读:
    ccmenu里的位置
    【luogu P3346】诸神眷顾的幻想乡(广义 SAM)
    Snow的追寻(线段树)(LCA)
    【bzoj 4303】数列 / T4(K-D tree)
    选课 / T3(组合数)(容斥)
    随机游走 / T1(期望)(树形DP)
    【luogu P3898】期望异或 / T3 / 大新闻(数位DP)(数学)
    【luogu P7295】Paint by Letters P(前缀和)(欧拉公式)(bfs)(对偶图)
    【luogu P7294】Minimum Cost Paths P(二分)(单调栈)(斜率)
    【luogu P7293】Sum of Distances P(线段树)(图论)
  • 原文地址:https://www.cnblogs.com/Przz/p/6501749.html
Copyright © 2011-2022 走看看