PIPT
在使用虚拟存储器的系统中,仍旧可以使用物理Cache,这是最保守的一种做法
处理器送出的虚拟地址(VA)会首先被TLB转换为对应的物理地址(PA),然后使用物理地址来寻址Cache,此时就像是没有使用虚拟存储器一样,直接使用了物理Cache,并且使用物理地址的一部分作为 Tag,因此将其称为 physically-indexed,physically-tagged Cache
缺点:
由于寻址TLB的过程也需要消耗一定的时间,为了不至于对处理器的周期时间造成太大的负面影响,可能需要将访问TLB的过程单独作为一个流水段,这样相当于增加了流水线的级数;
- 对于I-Cache来说,增加一级流水线会导致分支预测失败时有更大的惩罚(penalty);
- 而对于 D-Cache来说,增加一级流水线会造成 load 指令的延迟(latency)变大。
这种设计方法在理论上是完全没有问题的,但是在真实的处理器中很少被采用,因为它完全串行了TLB和Cache的访问。而实际上,这是没有必要的,因为从虚拟地址到物理地址的转换过程中,低位的offset部分是保持不变的;
VIPT
这种方式使用了虚拟Cache,根据Cache的大小,直接使用虚拟地址的一部分来寻址这个Cache(这就是virtually-indexed),而在Cache中的Tag则使用物理地址中的PFN(这就是physically-tagged),这种Cache是目前被使用最多的,大多数的现代处理器都使用了这种方式;
在这种设计设计中,访问cache和访问TLB是可以同时进行的;
会有3种情况发生:
- k>L + b,此时Cache的容量小于一个页的大小;
- k= L+b,此时Cache的容量等于一个页的大小;
- k<L+b,此时Cache的容量大于一个页的大小。
1. 前两种情况;
此时Cache容量小于或等于一个页的大小,直接使用虚拟地址中的[L+b-1:0]部分来寻址Cache,找到对应Cache line中的数据,并将这个Cache line的Tag部分和TLB转换得到的PFN进行比较,用来判断Cache是否命中。
这种设计可以避免虚拟Cache中的重名问题,因为它本质上使用物理地址来寻址Cache,相当于直接使用了物理Cache。
在物理地址中除了寻址Cache的index部分,剩下的内容就作为Tag存储在Cache中,这部分内容必须要经过 TLB才可以得到。访问 TLB 和 Cache 是同时进行的,当从 TLB 得到物理地址的时候,从Cache中也读取到了对应的Tag值,此时就可以进行Tag比较从而判断是否命中,如果处理器的周期时间允许的话,这些过程可以在一个周期内完成。
上面的例子其实提供了一个思考,要增加一个cache的容量,有哪些方式?
- 对于direct cache, 完全取决于index的大小;
- 对于组相联的cache, 因为cache_size = set_num * way_num * cacheline; 所以,在set_num不能改变的情况下,可以增加way_num的个数,因为way_num的增加,不会引起寻址cache所需要的bit的增加;
2. 第三种情况
这种场景的提出,就是因为上述的增加way的方式,不可能无限的增加,因此只能增加set_num的个数;
此时会导致L+b > k, 这种设计就是真正的virtually-indexed;
这种设计,会引入aliasing问题:Cache中两个不同的位置对应着物理存储器中同一个物理位置
除了之前提到的通过分bank的方式,来解决alising问题,还可以通过如下的方式来解决:
假设最开始时,虚拟地址VA1已经存在于L1 Cache和L2 Cache当中,当虚拟地址VA2访问 L1 Cache时,会发生缺失,于是就去L2 Cache中寻找数据。
- 在大多数处理器中,L2 Cache会直接使用经过TLB转化的物理地址(PA)来寻址L2 Cache,而这个物理地址已经存在于L2 Cache中了(因为VA1存在于L2 Cache中)
- 所以此时VA2发现它要找的数据存在于L2 Cache当中,但是此时还应该告诉VA2,在L2 Cache中,PA的数据其实是属于VA1的,因此需要在L2 Cache的每个Cache line中加以标记。
- 如图3.41所示的那样,在L2 Cache的每个Cache line中存储虚拟地址的a部分,这样当VA2被转换为PA并访问L2 Cache时,如果发现这个PA已经存在于L2 Cache中,并且这个PA对应的a不等于VA2中的a,那么就表明此时存在和VA2重名的虚拟地址(图3.41中即是VA1)
- 在将VA2的数据从L2 Cache读取到L1 Cache之前,应该使用L2 Cache中这个PA对应的a来寻址L1 Cache(VA=a+offset),找到L1 Cache中那个存放重名虚拟地址的Cache line,将其置为无效。
- 当然,如果发现此时这个Cache line是脏(dirty)的状态,那么还需要首先将它进行clean操作,也就是将这个脏的Cache line写回到L2 Cache中,然后才能将它置为无效
- 这样在L1Cache中就不会存在重名的虚拟地址,此时就可以将VA2的数据从L2 Cache读取到L1Cache中,而且能够保证在L1 Cache中不存在和VA2重名的情况。
VIVT
在这种方式中,直接缓存了从虚拟地址到数据的过程,它会使用虚拟地址来寻址Cache(也就是 virtually-indexed),并使用虚拟地址作为 Tag(也就是 virtually-tagged),因此这种Cache可以称得上是名副其实的Virtual Cache了。
如果Cache命中,那么直接就可以从Cache 中获得数据,都不需要访问 TLB;如果 Cache 缺失,那么就仍旧需要使用 TLB 来将虚拟地址转换为物理地址,然后使用物理地址去寻址L2 Cache,从而得到缺失的数据(在现代的处理器中,L2及其更下层的Cache都是物理Cache),这个过程如图3.42所示。