关于我转生为MMU,和我的傲娇Cache搭档一起实现双重加速这件事✨
B站播客
![[d297a5d22d9d43418719d6f8648de2e1.mp3]]
引言:CPU的“快”从何而来?
当CPU发出一个内存访问请求时,它需要经过MMU(内存管理单元)将虚拟地址翻译成物理地址,然后才能去Cache或主存中查找数据。这听起来是一个串行的、耗时的过程。但为什么现代计算机给人的感觉如此流畅?难道MMU的地址翻译是瞬时完成的吗?
虚地址索引、实地址标记(Virtually Indexed, Physically Tagged, VIPT) Cache,看看它如何实现MMU与Cache查找的“瞒天过海”式并行。
一、传统模式:访存的“龟速”瓶颈
在深入VIPT之前,我们先回顾一下最直观的访存方式。如果MMU和Cache完全串行工作,流程是这样的:
-
CPU发出虚拟地址(VA)。
-
MMU/TLB:用VA的高位(虚拟页号)去查页表,将VA翻译成物理地址(PA)。
-
Cache控制器:拿到完整的PA后,将其拆解成标记、索引和偏移量,开始在Cache中查找。
-
数据返回:如果命中,则从Cache中返回数据。
这个过程,我们称之为实地址索引、实地址标记(PIPT)。它的缺点显而易见:MMU的地址翻译是Cache查找的前提。如果页表查询或TLB未命中,就需要访问主存,这个巨大的延迟会严重拖慢整个系统的性能。
二、:一次精妙的“偷步”
虚拟地址的页内偏移量与物理地址的页内偏移量是完全相同的!这个偏移量在地址翻译过程中是恒定不变的。
利用这一点,VIPT Cache的设计思想应运而生:既然Cache的**组索引(Index)和块内偏移(Offset)都在地址的低位,那么我们能否让它们完全落入页内偏移的范围内呢?如果能,我们就可以在MMU翻译高位(页号)的同时,用虚拟地址的低位(页内偏移)**去同步完成Cache的组查找!
这就是VIPT的核心:虚地址索引——用虚拟地址的低位部分作为索引,来确定Cache的组号;而实地址标记——在完成翻译后,用物理地址的页号来与Cache中的标记进行比对,以最终确定是否命中。
三、VIPT的完美配合:工作流程详解
假设:
-
地址空间:32位
-
页面大小:4KB(212 B),页内偏移占12位。
-
Cache:16KB,4路组相联,块大小64B。
首先,我们来计算Cache的索引和偏移位数:
-
块内偏移:log2(64B)=6位。
-
Cache组数:16KB/(4×64B)=64组。
-
组索引:log2(64)=6位。
索引位数(6位) + 偏移位数(6位) = 12位,这正好等于页内偏移的位数!这说明我们可以完美地采用VIPT设计。
VIPT工作步骤:
-
CPU发出一个虚拟地址(VA)。
-
并行启动!
-
MMU/TLB:用VA的**高20位(虚拟页号)**去查表,获取物理页号。
-
Cache控制器:同时,用VA的低12位,将其中的6位作为索引,6位作为偏移,立即定位到Cache的一个特定组,并把该组中的所有行(4路)都读出来。
-
-
最终验证:
-
当MMU翻译完成,得到物理页号。
-
Cache控制器将物理页号和VA的高位组合成完整的物理地址标记。
-
与第2步中读出的Cache行中的标记进行比对。如果匹配,则命中!
-
四、避坑
1. 地址重叠陷阱
VIPT并非万能。如果Cache的索引位和偏移位之和大于页内偏移的位数,VIPT就会失效。
反例:如果页面大小仍是4KB,但Cache变成了64KB,块大小64B,4路组相联。
-
组数:64KB/(4×64B)=256组。
-
索引位数:log2(256)=8位。
-
索引(8位) + 偏移(6位) = 14位。
-
14位 > 页内偏移的12位!这意味着Cache索引的一部分(2位)需要从虚拟页号中获取,而这部分在MMU翻译前后是会变化的,因此无法并行。这种情况下,只能退回PIPT模式。