[笔记]CSAPP第五章 优化程序性能

2022/4/21 20:14:42

本文主要是介绍[笔记]CSAPP第五章 优化程序性能,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

第四章与CPU设计有关,暂时跳过,以后有时间再看。

  • 编写高效程序要点:
    1. 必须选择一组合适的算法和数据结构;
    2. 必须编写出编译器能够有效优化以转换成高效可执行代码的源代码;
    3. 针对运算量特别大的计算,将一个任务分成多个部分,在多核和多处理器的某种组合上并行地计算。(第12章内容)

优化编译器的能力和局限性

  • 内存别名使用

    • 在安全优化时,编译器必须假设不同指针可能会指向内存中同一个位置。
    // 如果xp等于yp,两个函数的效果会怎样?
    void twiddlel (long *Xp , long *yp)
    {
      *xp += *yp ;
      *xp += *yp ;
    }
    void twiddle2 (long *XP , long *yp)
    {
      *xp += 2* *yp ;
    }
    
  • 函数调用

    • 大多数编译器不会试图判断一个函数是否存在副作用(比如改变全局程序状态)。编译器通常总会假设最糟的情况,并保持所有的函数调用不变。
    • 可以用内联函数替换优化函数调用。

程序性能表示方式

  • CPE:Cycles Per Element,每元素的周期数。用时钟周期而不是具体时间来表示程序的性能。

提高性能的几种方法

消除循环的低效率

  • 循环内要执行多次但是计算结果不会改变的计算,应该从循环内部移动到循环前。
  • 有些结果不变的计算可能复杂度较高,并且处于函数内部,编译器难以进行优化,需要程序员手动优化。

减少过程调用

  • 过程调用会带来开销,并且妨碍大多数形式的程序优化。

消除不必要的内存引用

  • 引入临时变量来存储对某个指针的值,在之后的操作中改变临时变量的值,最后将临时变量的值写入指针,可以将对内存的访问转换为寄存器操作,提高程序性能。(访问寄存器比访问内存快得多)

循环展开

  • 通过增加每一次迭代计算的元素数量,减少循环的迭代次数。(编译器可以很容易地执行循环展开)

提高并行性

  • 对于一个可结合和可交换的合并运算来说,可以通过将一组合并运算分割成两个或多个部分,并在最后合并结果来提高性能。
  • 合适的重新组合变换能够减少计算中关键路径上操作的数量,通过更好地利用功能单元的流水线能力得到更好的性能。

一些限制因素

  • 寄存器溢出
  • 分支预测和预测错误处罚

理解内存性能

  • 加载(从内存读到寄存器)和存储(从寄存器写到内存)。

加载的性能

  • 包含加载操作的程序的性能既依赖于流水线的能力,也依赖于加载单元的延迟。

存储的性能

  • 存储操作不影响任何寄存器值。

  • 考虑下面两个调用A和B,A的CPE比B的CPE小,这是因为A中从*src读出的结果不受对*dest的写的影响,而B中存在参数srcdest都是指向数组元素a[0]的指针,这时指针引用*src的每次加载都会得到指针引用*dest的上一次执行存储的值。这就是写/读相关:一个内存读的结果依赖于一个最近的内存写。

    /* Write to dest , read from src */
    void write_read(long *src, long *dst, long n)
    {
      long cnt = n;
      long val = 0;
      while (cnt) {
        *dst = val ;
        val = (*src) + 1;
        cnt--;
      }
    }
    
    //example A
    write_read(&a[0], &a[1], 3);
    
    //example B
    write_read(&a[0], &a[0], 3);
    


这篇关于[笔记]CSAPP第五章 优化程序性能的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程