对页面5的访问并没有在Cache中命中,此时需要一个Free页面进行页面替换。LIRS算法首先淘汰在Q中页面7,同时将这页面在S中的状态更改为不在Cache命中;之后页面8从S落到Q中,状态从LIR迁移到HIR,但是这个页面仍在Cache中,需要重新压栈;页面5没有在Cache中命中,但是在S中命中,需要将其移出后重新压栈,状态改变为在Cache中命中。本篇不再介绍LIRS算法的实现细节,对此有兴趣的读者可以参照[40][41]。
而后出现的Clock-Pro算法是LIRS思想在Clock算法中的体现。Clock-Pro和LIRS都为LIRS类算法。其中Clock-Pro算法实现开销更小,适用于操作系统的Virtual MemoryManagement,并得到了广泛的应用,Linux和NetBSD使用了该算法;LIRS算法适用于I/O存储领域中,mySQL和Apache Derby使用了该算法。LIRS算法较为完美地解决了Weak Access Locality AccessPattern的处理。在LIRS算法出现之后,还有许多页面替换算法,这些后继算法的陆续出现,一次又一次证明了尚未出现更好的算法在这些领域上超越LIRS算法。
LIRS和Clock-Pro算法在这个领域的地位相当于Two-Level Adaptive BranchPrediction在Branch Prediction中的地位。在详细研读[40][42]的细节之后,发现更多的是作者的实践过程。LIRS算法类不是空想而得,是在试错了99条路之后的发现。这是创新的必由之路。
在掌握必要的基础知识后,也许我们最应该做的并不是研读他人的书籍和论文,更多的是去实践。经历了这些艰辛的实践过程,才会有真正的自信。这个自信不是盲从的排他,是能够容纳更多的声音,尽管发出这个声音的人你是如此厌恶。
与微架构设计相比,在操作系统和应用层面可以有更多的资源和更多的时间,使用更优的页面替换算法。虽然在操作系统和应用层面对资源和时间依然敏感,但是在这个层面上使用的再少的资源和再短的时间放到微架构中都是无比巨大。在微架构的设计中,很多在操作系统和应用层面适用的算法是不能考虑的。
假设一个CPU的主频为3.3GHz,在每一个Cycle只有300ps的情况之下,很多在操作系统层面可以使用的优秀算法不会有充足的时间运行。虽然LRU算法在Simplicity和Adaptability上依然有其优势,在微架构的设计中依然没有得到广泛的应用。即便是LRU算法,在Cache的Ways Number较大的情况之下也并不容易快速实现。当Way Number大于4后,LRU算法使用的Link Lists方式所带来的延时是Silicon Design不能考虑的实现方式。更糟糕的是,随着Way Number的增加,LRU算法需要使用更多的状态位。
下文讨论的Cache Block替换算法针对N-Way Set Associative组织方式。在这种情况下,Cache由多个Set组成,存储器访问命中其他Set时,不会影响当前Set的页面更换策略,所谓的替换操作是以Set为单位进行的。为简化起见,假设下文中出现的所有存储器访问都是针对同一个Set,不再考虑访问其他Set的情况。
通常情况,在N-Way SetAssociative的Cache中,快速实现Full LRU最多需要N×(N-1)/2个不相互冗余的状态位,理论上的最小值是Floor(LOG2(N!))个状态位[23]。因此当Way Number大于4之后,所需要的状态位不是硬件能够轻易负担的,所需要的计算时间不是微架构能够忍受的。这使得更多的微架构选用了PLRU算法进行Cache Block的Replacement。
与LRU算法相比,PLRU算法使用了更少的存储空间,查找需要替换页面的时间也较短,而且从Miss Rate指标的考量上与LRU算法较为类似,在某些特殊场景中甚至会优于LRU算法[36],从而在微架构的设计中得到了大规模的应用。
PLRU算法有两种实现方式,分别为MRU-Based和Tree-Based方式。MRU-Based PLRU的实现方式是为每一个Cache Block设置一个MRU Bit,存储器访问命中时,该位将设置为1,表示当前Cache Block最近进行过访问。当因为Cache Miss而进行Replacement时,将寻找为