预取技术是通过计算和访存的重叠,在Cache可能会发生失效之前发出预取请求以便在该数据真正被使用到时己提前将数据块取入Cache,从而避免Cache失效造成的处理器停顿。
预取技术是通过计算和访存的重叠,在 Cache 可能会发生失效之前发出预取请求以便在该数据真正被使用到时己提前将数据块取入 Cache,从而避免 Cache 失效造成的处理器停顿。
硬件预取是由硬件根据访存的历史信息,对未来可能的访存单元预先取入 Cache,从而在数据真正被用到时不会造成 Cache 失效。但是由于只是基于访存的历史信息,硬件预取会取回大量无用的 Cache 块,占用访存带宽,还会导致严重的 Cache 污染问题。由于硬件预取是基于访存的历史信息来预测未来的访存模式,从而可以在数据使用之前将其从下一级的存储器中取回。
当代微处理器大都提供了预取指令来支持软件的预取。软件预取是指在编译时由编译器显示加入预取指令,提前将下一级存储器中的数据取回。因为加入了大量的预取指令,同时显示的预取指令需要计算出准确的预取地址,从而导致不能及时的发出预取指令以足够隐藏访存延时,影响了性能的提高。并且必须使额外的 预取指令开销不能超过预取所能带来的效益, 否则得不偿失。
考虑到硬件预取和软件预取的缺点,有不少学者提出用软硬件结合的方法来解决这些问题。基本方法都是围绕软件给予硬件一些关于程序的信息,克服单纯的硬件预取的盲目性,从而可以得到更高的性能,更具有吸引力。 如提出的 GRP 技术,它是由编译提供预取的时机、预取的步长、预取的元素数目等信息,由硬件完成预取动作。
Cache 失效通常被分为三类,分别为强制性失效、冲突失效和容量失效。有很多技术关注的是减少冲突性失效,但是它们对于冲突失效和容量失效则不能解决。预取技术预测将会引起 Cache 失效的访存,利用存储器的空闲带宽,提前将其取回,从而隐藏由于访存延迟引起的处理器停顿。能够很好的解决强制性失效和容量失效。一般来说,CPU 并不直接与存储器交换数据,而是通过 Cache 间接进行。由平均访存时间公式和程序运行时间公式可以看出,Cache 失效对于系统的性能有着很大的影响。因此,为了改进系统的性能,首先必须要找出 Cache 失效的特点。 通过对 NPB 访存行为的研究,发现在 NPB 测试集中线性访存模式是造成 Cache 失效的最主要的原因。统计结果表明,由线性访存模式直接引起的 Cache 失效占程序总的 Cache 失效次数的 68.6%;而若考虑由此引起的 Cache 污染对失效率的影响该比例上升至 78.2%。同时,线性访存模式具有模式简单,便于优化的优点。因 此,在研究 Cache 行为、提高 Cache 利用率以及设计新的软件可管理的 Cache 结构时应该对这 类访存模式给予考虑。 对于线性访存模式所引起的大量失效,预取是个很好的选择。使用预取技术,能使数据和指令可在 CPU 使用之前到达与 CPU 更近的存储层次,因此在真正需要它们时能及时的拿到或者可以减少延时和阻塞的时间。但是预取面临着下面几个问题:
a. 什么时候进行预取;
b. 预取什么样的数 据;
c. 预取到的数据放在什么位置。
而如果没有解决好其中某些问题,则会导致:
a. 大量无用数据块的取回,以及由此导致的带宽的浪费;
b. 预取的数据造成了将要使用数据的替换,造成 Cache 性能的下降;
c. 预取时机过早或过晚,数据在没有使用前就被替换出去或者是要使用时还没有取回。
Cache 对于 NPB 这类计算密集型的应用性能发挥不足;这是由于程序中不同代码段的数据集具有不同的访存模式而 Cache 却对它们采用了统一的策略所造成的。
编译指导的失效时预取
一个好的预取技术的基础是能够准确的预测程序未来的访存行为。而线性访存模式对应为程序中访问的地址随时间按照线性规律变化,并且这类访存模式在科学计算、数据库、 多媒体等应用中占有很大的比例。恰恰也正是线性访存模式造成了程序中相当一部分的 Cache 失效。如果能够很好的解决线性访存模式的访存失效引起的大量 CPU 停顿,则可能会大大改善系统的性能。根据局部性原理,程序即将用到的数据块多数情况下与当前访问的数据块在空间上是相邻的或者是临近的。失效时预取技术 (Prefetch On Miss ) 就是利用这个基本原理,具体做法是在访存引起 Cache 失效时取回两个 Cache 块:请求的数据块和顺序的一下个数据块。
编译指导的基于访存预测表的预取技术
以上提出的编译指导的失效时预取技术和传统失效时预取技术相比,可以大大的提高预取的准确度,减少 Cache 的污染。但是,编译指导的失效时预取只有在访存发生 Cache 失效时才会启动预取动作,预取顺序的下一个 Cache 块。对于数组顺序遍历的访问模式,失效时预取 (预取度为 1)可以将失效率降低一半。但是可以看到,对于跨步式或是逆序的访问模式,失效时预取会糟糕。因为跨步式或是逆序的访问模式在访存发生 Cache 失效后不会在短时间内访问顺序的下一个 Cache 块,而失效时预取则不断的取回了大量的无用块,造成 Cache 污染。即使 增加了编译的指导对于这类访问模式也只能采取不预取的策略。但线性访存模式是最具有规律性的访问模式,很容易预测,同时在程序总访存量中占有很大的比例,所以应该采用更高级的策略来处理线性访问模式。如果硬件能够在编译所给的提示下,在 Cache 还没有发生失效时就能够预测到将会访问到的数据,并不断地将其提前取回,那么预取的性能将会大大的提升。