优化七:编译器优化,降低miss率
处理器和主内存之间不断扩大的性能差距促使编译器编写者仔细检查内存层次结构,看看编译时优化是否可以提高性能。再次,研究分为指令缺失的改进和数据缺失的改进。接下来介绍的优化可以在许多现代编译器中找到。
有些程序具有嵌套循环,以非连续的顺序访问内存中的数据。 只需交换循环的嵌套即可使代码按照数据存储的顺序访问数据。 假设数组不适合缓存,该技术通过提高空间局部性来减少丢失; 重新排序可以在缓存块中的数据被丢弃之前最大限度地利用它们。 例如,如果 x 是大小为 [5000,100] 的二维数组,且分配的 x[i,j] 和 x[i,j +1] 相邻(称为行主序,因为数组是按行),那么以下两段代码显示了如何优化访问:
原始代码将以 100 个字的步幅跳过内存,而修订版本会在进入下一个块之前访问一个缓存块中的所有字。 这种优化在不影响执行指令数量的情况下提高了缓存性能。
优化八:指令和数据的硬件预取,减少丢失惩罚或丢失率
预取技术是一种通过提前将可能需要的数据或指令从主存中取出,放入缓存或外部缓冲区中,从而减少缓存不命中的代价,提高处理器性能的方法。预取技术可以分为指令预取和数据预取,可以分为硬件预取和软件预取。具体来说:
- 指令预取通常是在硬件上实现的,不依赖于缓存。典型的做法是,在发生缓存不命中时,处理器会同时取出所需的块和下一个连续的块。所需的块在返回后放入指令缓存中,而预取的块则放入指令流缓冲区中。如果所需的块已经在指令流缓冲区中,那么原来的缓存请求就会被取消,直接从指令流缓冲区中读取该块,并发出下一个预取请求。
- 数据预取也可以采用类似的方法(Jouppi, 1990)。Palacharla and Kessler (1994) 研究了一组科学计算程序,并考虑了多个可以处理指令或数据的流缓冲区。他们发现,使用八个流缓冲区可以捕获50%~70%的所有缓存不命中,对于一个拥有两个64 KiB四路组相联缓存(一个用于指令,一个用于数据)的处理器来说,这是一个很好的结果。
预取技术依赖于利用原本未被使用的内存带宽,但是如果它干扰了需求不命中,它实际上会降低性能。编译器的帮助可以减少无用的预取。当预取技术工作得很好时,它对功耗的影响可以忽略不计。当预取的数据没有被使用或者有用的数据被替换时,预取技术会对功耗有非常负面的影响。
下图为由于 Intel Pentium 4 上的硬件预取而加速,并且针对 12 个 SPECint2000 基准测试中的 2 个和 14 个 SPECfp2000 基准测试中的 9 个打开了硬件预取。 仅显示从预取中获益最多的程序; 预取使缺失的 15 个 SPECCPU 基准测试速度加快了15%。