CPU缓存实现方式:VIVT、VIPT和PIPT

处理器在进行存储器访问时,处理器访问的是虚拟地址,经过TLB和MMU的映射,最终变成了物理地址。那么在查询cache组的时候,需要使用虚拟地址的索引域或者物理地址的索引域(Index),同样地,在查询到cache组之后,可以使用虚拟地址的标记域或者物理地址的标记域(Tag)来匹配cache line. cache可以设计成通过虚拟地址或者物理地址来访问,这个在处理器设计的时候已经确定下来,可以分成如下三种:

  • VIVT(Virtual Index Virtual Tag):使用虚拟地址索引域和虚拟地址的标记域。
  • VIPT(Virtual Index Physical Tag):使用虚拟地址的索引域和物理地址的标记域。
  • PIPT(Physical Index Physical Tag):使用物理地址的索引域和物理地址的标记域。
    先来看一下VIVT,这种缓存模式直接使用虚拟地址的索引域和标记域来查找cache line,不经过MMU的翻译,所以查找速度会快一些。然而VIVT的方式可能会导致缓存别名(cache aliasing)的问题,也就是多个相同的虚拟地址可能会被映射到同一个物理地址(synonyms)例如进程间通信,这会导致指向同一个内存地址的虚拟地址的缓存被分开存储,所以可能产生一致性问题,比如更改了一个虚拟地址的缓存行后,与它指向相同物理地址的虚拟地址的缓存行没有更改,导致数据不一致。还有可能是相同的虚拟地址指向不同的物理地址(homonyms)(比如说在不同的进程中),仅仅依靠Virtual Index是不能区分这种情况的,不过可以通过清除缓存来解决这一问题(在进程切换的时候不清除缓存),或者可以为每个虚拟地址空间加个标记(ASID)来区分不同的地址空间。另外还有一个问题是当虚拟地址到物理地址的映射发生变化时,也需要清除一些缓存。所有的这些问题在使用VIPT之后都会迎刃而解。
    再来看一下VIPT,VIPT使用虚拟地址的索引域和物理地址的标记域来查找cache line,它可以避免homonyms问题。至于synonyms问题,以Linux kernel为例,它是以4kb为页面进行管理的,那么对于一个页来说,虚拟地址和物理地址的低12bit是相同的,所以不同的虚拟地址映射到同一个物理地址的时候,这些虚拟地址的低12bit也是相同的,在这种情况下,如果索引域在0~12bit之内,那么这些虚拟地址的cache组是相同的,同样的它们的Physical Tag也是相同的,那么这些不同的虚拟地址的cache line在cache里面是同一个,因此不会产生一致性问题。另外,VIPT其实相比VIVT也没有太大的性能缺失,按照正常的TLB命中率和一些内部的探测手段可以绕过地址翻译,那么VIPT和VIVT相比起来性能也差不多。
    最后看一下PIPT,这种方式下索引域和标记域都采用物理地址,这种方式也可以避免缓存别名问题,不过性能方面要差一些,因为要先经过地址翻译的过程。
    缓存对于CPU的性能来说很关键,所以一些L1-cache使用的是virtual index,这样至少能和TLB的查询过程一起并行地从cache中取出一组数据,然后根据TLB的结果从中选择Tag相同的那条数据。但是virtual index并不是对于所有级别的缓存都是最佳选择,缓存别名问题对于越大的缓存解决起来就越代价越大,所以一些二级或更大的缓存使用的是physical index.在过去CPU曾经使用过virtual tag和physical tag,但是现在使用virtual tag的已经不多了,毕竟如果TLB查询能够在从cache中返回数据之前结束的话,那就不需要使用virtual tag了。大的缓存都倾向于使用physical tag,只有一些小的、低延迟的缓存才使用virtual tag。在最近的通用CPU中,virtual tag已经被vhints所取代。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章