《深入理解计算机系统》第4周

上周第三章已经看完,因此这周选择了加看第五章优化程序性能,通常编译器可以代替我们进行程序优化,例如循环展开、代码移动等。但是由于函数存在编译器无法感知的副作用,往往还需要程序员在编写代码时注重效率。

一些看上去无足轻重的代码段可能隐藏着渐进低效率(asymptotic inefficiency),在数据量小时测试可能无法发现问题,但部署环境后代码会突然成为瓶颈。有经验的程序员工作的一部分就是避免引入这样的渐进低效率。

比如在循环中止条件我们经常会用到 strlen(s)来作为循环中止条件,而strlen所需时间和字符串长度N成正比,如果每次循环迭代前都判断一次,那么循环的整体运行时间会是NN。此时我们如果将字符串长度提前进行计算,再进行循环判断就会显著提升性能。书中实现对1048576字符进行测试,循环运行时间从17分钟降低到2毫秒, *50W倍左右的提升

当前GCC版本会对整数运算执行重新结合,但不是总有好的效果,通常循环展开和并行地累计在多个值中是提高程序性能的更可靠的方法。

CPE Cycles Per Element,作为一种表示程序性能并指导我们改进代码的度量标准。可以帮助我们在更细节的级别上理解迭代程序的循环性能。

未经优化的代码通常效率比较低,简单地使用命令行选项就能显著地提高程序性能(超过两个数量级)。

代码移动,识别要执行多次(如循环内)但是计算结果不会改变的计算,将其移动到代码前不会多次求值的部分来提高性能。优化编译器会尝试进行代码移动,但是不能可靠地发现一个函数是否会有副作用。因此为了改进代码,程序员必须经常帮助编译器显示地完成代码移动。

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

尽量消除代码中不必要的内存读写,用寄存器来保存中间值来提升性能

要进一步提高性能,必须考虑利用处理器微体系结构的优化,也就是处理器用来执行指令的底层系统设计。要想充分提高性能,需要仔细分析程序,同时代码的生成也要针对目标处理器进行调整。

延迟界限(latency bound),在下一条指令开始之前,这条指令必须结束。当代码中的数据相关限制了处理器利用指令级并行的能力时,延迟界限能够限制程序性能。

吞吐量界限(throughput bound),刻画了处理器功能单元的计算能力,这个界限是程序性能的终极限制。

整体操作:

  • ICU从高速缓存中读取指令,通常会在当前正在执行的指令很早之前取指,才有足够的时间对指令进行译码并把操作发送给EU。
  • 当程序遇到分支时,通过分支预测猜测是否会执行分支,并且同时预测分支的目标地址。
  • 使用投机执行的技术,取出位于它预测的分支会调到的地方的指令,对指令进行译码,甚至在确定分支预测正确之前开始执行。
  • 如果分支预测错误的话,会将状态重新置于分支点的状态,接着往下执行。

功能单元的性能:由延迟、时间、容量来刻画。

  • 延迟latency,表示完成运算所需要的总时间;
  • 发射时间issue time表示两个连续的同类型运算之间需要的最小时钟周期数;
  • capacity表示能够执行该运算功能单元的个数。 其中除法不是完全流水线化的,发射时间=延迟 即在开始一条新运算之前,除法器必须完成整个除法,使得其成为一个相对开销很大的运算。

循环展开通过增加每次迭代计算的元素数量,减少循环的迭代次数。编译器可以很容易地执行循环展开,只要优化级别设置得足够高。GCC -O3就会执行。

提高并行性:

对于可结合和可交换的合并运算来说,通过将一组合并运算分割成两路或多路,最后再合并来提高性能。

重新结合变换打破顺序相关从而使性能提高到延迟界限之外的方法,减少计算中关键路径上操作的数量,更好地利用功能单元的流水线能力。 (大多数编译器不会对浮点运算重新结合,因为这些运算是不保证结合的)

其他一些制约程序在实际机器上性能的因素

  • 寄存器溢出,并行度p超过了可用的寄存器数量,编译器会溢出,将某些临时值存放在内存中。此时再进行循环展开就没有效果,甚至会导致性能变差。
  • 分支预测错误处罚, 大约19个时钟周期,赌注很高。因此注意以下通用原则:
    • 不要过分关心可预测的分支;
    • 书写适合条件传送实现的代码

总结来说,优化程序性能的基本策略如下:

  • 高级设计: 选取合适的算法和数据结构,避免使用那些会产生渐进低效率的算法OR编码技术;
  • 基本编码原则:避免限制优化的因素,使编译器产生高效地代码。
  • 消除连续的函数调用:代码移动;
  • 消除不必要的内存引用: 引入临时变量;
  • 低级优化: 结构化代码以利用硬件功能;
    • 循环展开
    • 累积变量、重新结合等技术,提高指令级并行
    • 功能性的风格重写条件操作,使得编译器采用条件数据传送。