[笔记]CSAPP第五章 优化程序性能
作者:互联网
第四章与CPU设计有关,暂时跳过,以后有时间再看。
- 编写高效程序要点:
- 必须选择一组合适的算法和数据结构;
- 必须编写出编译器能够有效优化以转换成高效可执行代码的源代码;
- 针对运算量特别大的计算,将一个任务分成多个部分,在多核和多处理器的某种组合上并行地计算。(第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中存在参数src
和dest
都是指向数组元素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);
标签:src,CSAPP,性能,long,编译器,第五章,笔记,yp,内存 来源: https://www.cnblogs.com/fusheng-chana/p/16158562.html