欢迎光临专业集成电路测试网~~欢迎加入IC测试QQ群:111938408

专业IC测试网

当前位置: 网站主页 > 相关技术 > 芯片制造 >

【芯片设计】SoC 101(六):Memory

时间:2024-06-20 21:15来源: lawliet 芯时代青年 作者:ictest8_edit 点击:

 

本篇文章将给大家带来Memory相关的内容。我之前的文章写了很多Memory相关的内容,包括Cache、虚拟内存等。感兴趣的可以去看我之前的文章,本篇文章就当是我自己复习这一部分内容了。

 

本篇文章将基于下面的内容进行展开。其重点基本就是Cache和虚拟内存。

 


1、Introduction to the memory hierarchy


首先我们来看一下存储器的层次结构。即使不是芯片工程师或者计算机相关的技术人员,应该也知道内存和硬盘。内存容量通常比较小,硬盘容量比较大。为什么要这么设计呢?就不能用一种很大的吗?答案就是不能,大的存储器是为了尽可能存储更多的内容,而小的存储器相对较快,能够提升用户的体验。对于存储器而言,大和快是矛盾的。通过合理的层次划分,来营造一种又大又快的存储器假象。

就好比你会把你现在正在看的两本书放到桌子上,其它不怎么看的书放在书架里面。你无法将所有的书都放在桌子上,你也不想把每天看的书放到书架里面。存储器的设计就是参考现实的储物模型。对于芯片工程师而言,最重要的几种存储器,寄存器,SRAM,DRAM以及FLASH。

 

DRAM相比于CPU,其速度是非常慢的。尤其当你用最新款CPU配旧的内存条的时候,你会发现你的电脑非常的卡。这本质上是工艺提升决定的。随着制程工艺的提升,处理器的性能提升大致符合摩尔定律。而存储器本身和CPU就有较远的物理距离,根据传输线模型,我们知道其传输无论如何也不可能超过物理极限。(没有学过数字集成电路的可以假设内存和CPU的传输速度是光速,通过物理距离除以光速就知道传一次数据要多久,再对比一下CPU主频,大家应该能有个直观地感受)

怎么办呢?如果只用DRAM,那么根据木桶效应,无论处理器的速度有多快,其实际体验也会很差。

 

我们都知道目前AMD和英特尔等公司,仍然在卷处理器的性能,那么相比上面的问题得到了解决,其是怎么解决的呢?我们先来看一下典型的存储器访存模式。

处理器本质上只是执行指令的工具。因此我们以一个非常典型的程序执行过程为例。一个常见的程序,从头执行到尾,然后会调用函数,存在一些分支,存在一些循环。对于循环而言,从头执行到尾,又从尾回到头,来来回回调用同一部分代码。如果这一部分代码还需要从DRAM读取,那就太花时间了。就好比你每晚都要看同一本书,你肯定不会把这本书放到一个隐秘的角落,看的时候才拿出来。你大概率会把这本书放在床边和桌子上,方便你每晚去拿书。这就是所谓的时间局部性。

对于计算机的程序和代码,也都是同样的道理。那种会反复调用的程序,反复读取的数据。我们可以将其备份起来放在一个方便读的地方。除了这一点以外,对于矩阵,大型的图像等等,我们往往会顺着连续读取,先读取其前八分之一,再读取八分之一,于是我们可以在用前八分之一的时候就可以将后面那八分之一提前准备好。这就是空间局部性。(对于目前流行的AI芯片设计而言,时间局部性和空间局部性也非常重要,AI芯片的Local Buffer基本都是利用这一点)

 

有关局部性我们还可以看看下面这张图片。正是因为局部性的存在,Cache才有意义。Cache的本质就是用一个相对小的存储器,营造出一个完备的大存储器的假象。

 

我们都知道,存储器的大小是固定的,Cache既然这么小,为什么能够营造一种很大的假象呢?那是因为对于用户而言,或者说对于一段程序而言,其某个时刻往往就只需要用很多数据的一小部分,如果你的Cache能够一直做到,刚好就存储了那一小部分。这就营造了一种Cache很大的假象,因为要啥有啥。

还有一点值得说的是,Cache是一个相对的概念,软件也可以实现Cache,你浏览器的缓存记录也是Cache。对于芯片和计算机而言,Cache通常是个硬件概念,大部分时候在讲SRAM,有时候也可以包括DRAM,但具体指的是什么要看具体的语境。

 

我们看一下下图,可以看到Cache就是一个中间载体,让CPU和主存储器不要太远了。其本质也是一种抽象。

 


2、Cache Organization


我们知道了为什么要有Cache,接下来我们看一下Cache的结构。

 

基于局部性原理,我们设计了Cache。Cache存储着最有可能被CPU访问的数据。

 

Cache是更高一级存储器的子集,当访问数据的时候,我们会优先去看Cache。我们怎么知道Cache存储的这个数据,就是我们需要访问的那个数据呢?为了确认这一点,我们除了数据内容以外,还需要有额外的验证信息。这个验证信息能对上了,才说明这个数据是要访问的数据。我们可以简单理解为ID号。

当能够在Cache里面访问到的时候,我们称为HIT,否则称为MISS。如果没有HIT,那只能去DRAM读取数据了。这样访存时间就大大增加了。因此我们要尽可能的提高命中率。或者更直白点,我们希望尽可能减少平均访存时间。

 

什么是ID?首先我们搞清楚一点,假设没有Cache,只有DRAM。那么就是基于地址访存对应的数据,也不存在用什么ID了。因为每个数据都是独一无二的,不存在一个地址有可能对应好几个数据。为什么Cache要有ID呢?因为Cache本质上就是DRAM的一小部分。可能对应Cache的前一百分之一,也有可能对应Cache的后一百分之一,你单纯访问Cache,不知道这个数据是不是你希望访问的地址要访问的数据。

举个简单的例子,假设DRAM有三个地址分别是1、2、3,Cache只有一个地址,假设存的是DRAM地址2的数据。你想读取地址1的数据,此时Cache返回的是地址2的数据,不是你期待的数据。怎么办呢?那就是用开始说的ID,Cache不光返回一个数据,它还有个ID,叫做2。这个时候你一看,哦,不是地址1,那不是我想要访问的数据,我还是得访问DRAM才能读到对应的值。

通过这个例子,想必大家明白了为什么需要ID。我们接着看,为什么不直接用地址作为ID呢?我们看下图,假设地址的宽度是32-bit,则一共有4GB个元素,每个元素的大小是1Byte。假设Cache只有1024个元素。Cache的数据占据了1024*1=1KB。而IDs居然占了32KB的存储容量。这是否有点本末倒置了?我们用了三十三分之三十二的容量去验证,只有三十三分之一的容量存储实际的值,这显然太浪费了。

 

为了尽可能有效利用数据,很显然应该让Data数据多一点,IDs相对少一点。我们看下面这个例子,可以让一行Cache占64Bytes,即2的六次方,这样每个Cacheline都是64Byte对齐的,那么Tag就不需要那么长,只需要26bit就可以了。(后6bit地址都是0,比不比都无所谓的)

 

上面这个例子就是所谓的全相联,因为它只有Tag和Offset,我们根据地址,不知道去访问哪一个Cacheline,每一个Cacheline都可能对应这个数据。因此需要一起比较,看Tag是否一致。

 

举个简单的例子,Tag就类似于书名,Offset就类似于在书的哪一页。你只有这两个信息的话,让你去书柜找书,你需要每个书都对比,才知道是不是你要找的书。这样做有什么优点呢?优点是书想放到哪里就放到哪里,不受限制,也就是不容易冲突。而后面介绍的组相联或者直接相联,还有个index,这个index就类似于书存放在哪一行。我们直接看下图,非常的清晰。

 

全相联需要并行比较,不适用于数据项特别多的情况,CAM本质上就是全相联存储器。对于Cache而言,组相联用的最多。理论上Cache的Way越多,越不容易冲突,但相应的性能越差。这都是一种trade-off。

 

直接相联就是1-way的组相联,其很容易发生碰撞,但性能很好,读取很快。

 

我们来看个总结,如下所示。比起这个例子我更喜欢上面的书架图。

 

总结一下Cache术语:Tag、Index、Offset。如果你能够很清楚的理解这三个概念,那么恭喜你,你已经掌握了Cache的基本逻辑了。

 


3、Tradeoffs in Cache Design


我们来看一下,Cache什么时候会发生缺失。3C定律由计算机体系结构大牛Mark D. Hill提出。首先是Compulsory缺失,理论上第一次启动的时候,Cache是空的,这个时候肯定会MISS。但实际上可以通过Prefetch缓解这个问题,这里不详细讲。

然后是Capacity Misses,就比如你需要来回读一段2KB大小的数据(完全随机读取顺序),你的Cache只有1KB,那肯定会MISS。

还有就是Conflict Misses,比如一直随机访存三组数据,这三组数据映射到同一个Set,你的Set只有两个Way,那么也会发生MISS。

通过合理的设计,即使容量没有增加的情况,也可以缓解后面两种MISS。

 

我们再巩固一下3C定律,大家直接看图。

 

我们看一下Cache是如何影响计算机性能的。假设理想的CPI为2,即平均下来每条指令需要两个时钟周期。假设指令Cache的MISS率为百分之2,当发生MISS以后会造成额外的100个时钟周期的损失。而数据Cache的MISS率为百分之4,只有Load和Store指令会访问数据Cache,假设占所有指令的百分之三十六。平均下来可以发现此时的CPI为5.44。

因为Cache MISS的存在,实际CPI会比理论的CPI大很多,性能远远没有达到我们想要的程度。为什么呢?尽管出现MISS的概率并不高,但一旦出现MISS,就需要访问2级Cache甚至是外部的DDR,这个周期就是几百个周期往上了。所以尽可能减少MISS,即便出现了MISS也尽可能让损失小一点。

 
开始我们讨论的情况只有一级Cache,为了减少Cache MISS的损失,我们往往会设计多级Cache,用来兜底,同时也让Cache和DDR的距离没有那么远。对于L1 Cache而言,其关键在于快,而对于L2和L3Cache而言,其关键应该在于大而全。尽可能减少MISS,少访问DDR。

 

我们来看一个实际例子,在增加L2 Cache以后CPU性能是如何提升的。我们假设理想的CPI是1,时钟频率是4GHz,百分之二的指令会出现Miss,当出现Miss的时候会消耗额外的100ns。当增加L2 Cache以后,当出现L1 Cache的情况下会先访问L2 Cache,L2 Cache和L1 Cache都不命中的概率为百分之零点五。可以看到CPI显著降低,系统性能得到了提升。

 

接下来我们看一下Cache的替换策略。只有全相联和组相联存在Cache替换策略,直接相联一一对应,不存在替换哪一个的问题。这里不详细介绍,大家只要知道Cache替换策略用的最多的是随机替换和伪LRU即可。

 

接下来我们看一下写策略。我们思考这么一个问题,当我们需要往一个地址写一个数据的时候,此时发现Cache并没有这个地址对应的数据。这种情况下则为写MISS,理论上此时要更新Cache,这样下次读这个地址就能从Cache访问到了,不需要去读DDR。但问题在于DDR是不是也要更新?如果不更新,DDR的数据和Cache的数据可能不一致,对于写直通而言,需要同时写Cache和DDR。可以说有点费时费力,但逻辑很简单,数据也保持一致。

另外一种方法是只写Cache,既然只写了Cache,那肯定DDR的内容和Cache的内容大概率不一样了,此时需要用一个Dirty Bit标志,说明这个数据在主存和Cache中不一致。

写回通常和写分配配合使用,什么是写分配呢?写分配指的是当发生写MISS,除了把所写字写入主存之外,还要把包括所写字在内的一个块从主存读入Cache。非写分配则不用写Cache。如果不分配的话就没有写Cache,谈何写回呢?

全写和非写分配搭配,全写也就是cache和主存都要写,不分配的话只用写主存,一分配的话主存和cache都要写了,任务多了一倍,所以干脆不分配了

 

通常,我们还需要valid bit,来标志Cache的数据是不是有效的。为什么呢?因为我们重新跑一个程序的时候,SRAM的数据可能还是旧的,或者是系统刚上电,SRAM是随机的数据,实际上这些数据都是废数据,但SRAM实际上是无法复位的,我们只能刷SRAM,写成全0。完全可以用1bit的valid bit,来标志数据是否有效,其往往由寄存器阵列组成,可以直接复位。也可以用SRAM实现,作为额外的阵列,这样刷起来也很方便。

 

我们总结一下,Cache设计中的trade off。首先是Cache Size和Cache Block Size。Cache Size即总容量,其容量越大,理论上越不容易出现MISS,但相应的,大就是慢,其访存速度会下降。

然后是Cache Block Size,即一个index对应的一块Cache有多大,理论上这个越大,其可以越好的利用空间局部性。但在Cache Size总量不变的情况下,一块Cache大了,那么Cache数量就少了,Way也有可能变少了。这样就不能很好的利用时间局部性。

 

然后就是相联程度,写策略和替换策略。这里可以展开的话题就太多了,可以看我之前的文章或者找一些相关的资料。

 


4、Virtual Memory


这一部分可以看我之前写过的虚拟内存文章。此处就当巩固复习了。

在传统的处理器中,我们只有物理地址。什么是物理地址呢?就是真真实实分配的地址。具有绝对性,唯一性。其映射到存储器的哪一个点。这就引出了一个问题,什么问题呢?

不知道大家有没有这样的经历,小时候玩电脑,内存可能只有两个G,但游戏动不动就是七八个G,我们已经知道了程序只有搬运到内存才能跑起来,位于硬盘的话是跑不起来的,那这是怎么做的呢?实际上即使游戏有很多个G,你现在只运行了程序的一部分,所以不需要那么大的内存,也是可以运行的。真的需要运行的话,就再把剩下的搬到内存即可。这也就是为什么,以前玩那种RPG游戏,切换一个场景有可能非常卡,实际上就是在给你搬数据。

 

我们看使用物理地址有哪些问题,首先是Memory Size的问题。

理论上32bit的计算机寻址范围是4GB,如果内存没有4GB的话,那就有可能读到内存以外的地址。此时就会报错,如果计算机真的这么鸡肋,那大型游戏也无法那么早就出现了。

 

此外还存在进程隔离的问题,理论上程序都认为自己占据了整片内存。除非手工分配,否则两个程序很有可能操作同一内存地址的数据。当同时运行多个进程的时候,这个问题会非常明显。

我们不可能显示指定程序占用哪一块存储区域,除非是写没有操作系统的嵌入式程序,才有可能这样。对于大型程序而言,如果还要一直考虑这一块程序应该占用哪一块内存之类的问题,那开发效率也太低了。并且程序可以操作任意一块物理地址,本身就是非常危险的事情。如果这一块地址有重要数据,那么恶意代码就有可能窃取或者改写这一块数据,我们显然是无法接受这一点的。

 

早期的操作系统,尝试用分段的方式去解决这一问题。所谓分段,就是把内存地址空间分为很多很多块。各个进程使用不同的段,理论上这样确实可以解决问题。但其有着非常显著的问题。首先就是空间可能不够,内存本身就很少,再分成多段,就更少了。很容易出现segmentation fault。并且不同的程序,实际上很有可能使用同一块地址的数据,使用这一方法就很难做到数据共享。另外就是碎片化,其分段是固定每一段的大小,有些程序实际上非常小,那就浪费了很多空间,这就是所谓的碎片。

 

目前的操作系统,基本不会使用这一方式实现地址的重新映射了。

 

下面这个例子说实话我没看懂,不知道要表达什么。可能是要表达一种间接寻找的思想吧。

 

为了解决物理地址存在的问题,那就对物理地址做一个变换。这就是所谓的虚拟地址。我们看下图,对于采用物理地址的方法,程序直接指向真实的物理地址,很容易出现内存不够的现象。而虚拟地址会通过MMU转换为真实的物理地址,这个映射是有可能映射到磁盘或者其它主存的,需要使用的时候再搬到内存即可。

 

我们把内存分成一块一块的页,通常一页是4KB。虚拟地址到物理地址都是这样的一块一块的页,而存放虚拟地址到物理地址映射关系的表,称为页表。

 

页表如下所示,可以看到页表本质上就是一个表,存放着映射关系。从虚拟的page number到物理的page number。假设我们需要访问某个地址的时候,首先需要访问页表,得到真正的物理地址,再通过物理地址去访存Memory。

 

在使用虚拟地址之前,我们的访问都是针对物理地址。但前面已经说过了,物理地址可能不够用,此时就会出错。而如果使用虚拟地址的话,我们把所有的数据都放到磁盘里面,这样肯定能放下(磁盘都放不下那确实没办法了),然后将现在要用的放到内存里面。如果内存不够用了,此时页表没有对应的映射关系,就说明该数据不在内存里面,而是在磁盘里面。这就是Page fault。处理该过程需要很多很多周期。

 

这一过程由操作系统去处理。当出现page fault的时候,会唤醒操作系统。如果DRAM已经满了的话,操作系统会选择特定的page,把它赶出去,这一过程就类似于Cache中的替换。然后通过swap page操作,把新的page从磁盘读到内存里面。然后更新页表,回到出现异常的指令那里,重新执行。

为了减少page faults开销,会使用一些类似于Cache的机制,这里不详细讲解。

 

接下来我们看看虚拟内存是怎么解决我们之前遇到的问题的:,首先就是没有足够Memory的问题。在使用虚拟内存之前,会碰到RAM不够用的情况。就比如DRAM只有1GB,并且没有使用虚拟内存。如果地址映射到别的地方,那就会出错。但有了虚拟内存就没有这个问题,除了DRAM还有硬盘的存在,硬盘和DRAM共同组成存储空间,对于普通应用而言,其是感受不到这一点的(当然访问时间不一样)。

这就好比你要来回的看四本书,但是你的书桌只能够放一本书,这个时候书架就相当于硬盘,图书管理员就是那个操作系统。当发现你要换书了,赶紧把你要看的那本书从书架搬到你的书桌上。

 

另外一个问题就是程序的隔离和共享问题。如果没有虚拟内存,大家访问的都是同一块物理地址空间。那就有问题了,别的进程就有可能改写另一个进程的数据。就好比你正在看一本书,另外一个人也用这个书桌,它直接把你这本书换成他的书,那你肯定是没法接受的。但如果采用虚拟内存,大家都有一个假象的空间。这一问题就解决了。

除了隔离,虚拟内存还可以实现数据共享。比如A程序和B程序,都调用了Printf函数,编译器可能编译出来的printf函数在不同的地址,但这个地址是虚拟地址。操作系统很聪明,把这两个虚拟地址映射到同一块物理地址,这个物理地址就是printf函数存放的地方。

 

然后就是内存的碎片问题即Fragmentation,基于虚拟内存。我们把存储空间分成了一页一页的大小,用完以后就可以释放掉。而不是像之前,一个程序开辟1GB,另外一个程序也开辟1GB,实际上它们可能只有了1KB,这样就有大量的空间被浪费。使用页表,最多也就浪费几页。

 


5、Practical Paging


上面讨论的都是一两个进程,实际上大家可以打开任务管理器,看一下电脑有多少个进程正在执行。我们之前就知道,不同的进程使用的是不同的页表。我们先来看一下一个页表有多大。

页表存放的是地址映射关系,一页的大小本身是4KB,如果需要覆盖4GB的内存,那么理论上就需要1M的表项数量,一个表项即Entry是4B,因此总共需要4MB。是不是听上去还行?但实际上,你的电脑大概率是几百个进程同时在运行,这样就需要几个GB的页表,这显然是不可能的,我们不可能用内存的大多数存放映射关系,那样是本末倒置。

 

既然使用4KB大小的页,会导致页表的容量很大。那么按理来说,如果使用更大的页,那么页的总数就变少了,映射关系也就变少了,页表大小自然而然也变少了。这个方法早期是有使用的。对于32位机器而言,使用上述的方法可以有效减少页表的大小。

但我们看一看它的缺点,太大的页会造成碎片问题,即你没有用这么多,但是需要开辟这么多。并且一旦出现page fault,其搬运成本也更大。当然页很大,同样也可以减少page faults。并且磁盘是支持突发传输的,所以对于大笔数据的传输,开销没有那么大。但这个方法,对于64位机是不可接受的。我们想一想,即使你使用4MB的Page,那么其Offset是22bit,大家可以算一下2的64次方有多大,然后除以4MB,就是需要的页的个数,此时页表居然有32TB!所以我们必须想新的方法。

 

我们想一想上面的方法,其问题出在哪里,有一个很明显的问题。对于一个进程而言,其需要做到虚拟地址和物理地址的一一对应。但我们想一想,你有可能只用了4GB中非常小的一部分,每个进程都开辟这么大的列表,非常的浪费。

这里我简化一下描述,大家能理会意思就行。比如说虚拟地址有10000份,那么对应的物理地址也有一万份。那么页表就需要10000个表项。实际上你只用了100份,怎么少用一点呢?按理来说,你使用的这一百份大概率是连在一块的,我们是不是可以认为这10000份是100个100份?我们第一次用的时候,先基于当前地址属于100大份的哪个,基于这个大份给它开辟100小份。这样如果你的地址都在这一大份之内。那么你只需要用之前的一百分之一的大小开辟页表就可以了。

上述思路基本就是间接,加上用多少开辟多少。这也是目前的处理器中非常常见的方式。

 

我们来看一下RISC-V中的虚拟地址映射,这一部分其实就是简单介绍相关的CSR寄存器,以及RISC-V中的页表长什么样,大家自己看一下就行。

 

我们对比一下Virtual Memory和Cache。对于Cache而言,我们将其分为了一个一个的Block,Block通常为64B的大小。而对于虚拟内存,我们以页的粒度去处理,目前页的普遍大小为4KB。具体的大家可以看下图。

 


6、TLB


我们发现,在引入页表机制以后,一个指令的执行会变得非常繁琐,尤其是page fault的时候。跑一条指令可能非常非常久。有什么解决办法吗?

 

我们看一下为什么引入虚拟内存指令的执行变得这么复杂了。在使用虚拟内存之前,所有的地址都是真实的地址,都不需要转换,地址也都具有唯一性,它就对应于现实存储器中的某一块。不会有任何歧义。使用虚拟内存以后引入了转换的概念,尤其是使用页表。我们获取这个转换关系需要去访问DRAM,这很明显不太可以接收。有什么方法可以解决吗?Caching!

这种Cache我们称为TLB,请记住,TLB就是特殊的Cache,是页表的Cache。其基于虚拟地址映射。

 

为什么TLB是有效的呢?同样是因为局部性原理,所以我们访问的地址可能都是连续的,或者是相同的。这样TLB便可以发挥其作用。TLB通常采用多路组相联或者全相联,以减少MISS rate。

我们看下面这个图,访问0x00003204这个虚拟地址,其会和TLB所有的entry进行比较,因为是全相联结构。我们发现tag比对通过,所以我们就可以拿出其对应的PPN,这个PPN就是Cache中的data,0x2345,和offset即0x204拼接一下即组成了最终的物理地址。

 

现在我们的处理器有了TLB,可以看到,一切似乎变得简单的起来。但好像还有不足之处。。。

 

我们来想象,CPU如何去访问一个数据,就拿取指令来说吧。首先PC GEN产生一个虚拟地址,这个虚拟地址要经过TLB翻译成物理地址。如果TLB MISS的话则需要访问页表,我们假设没有MISS。现在拿着这个物理地址去访问Cache,如果Cache MISS的话则进一步访问下一级Cache。我们假设一切都是理想情况。

PC产生虚拟地址,虚拟地址通过TLB翻译为物理地址,物理地址访问Cache,Cache返回需要的数据。这个过程太慢了,在不考虑MISS的情况下,上述过程都起码要六七个周期,虚拟地址显著降低了Memory Access的速度。有没有更好的办法?

 

上述的机制我们称之为PIPT,即一切都基于物理地址访存。虚拟地址必须转换为物理地址才可以访问存储器。如果直接用虚拟地址访问Cache呢?可不可以。

当然是不可以的,虚拟地址直接访问Cache,Cache不知道有没有权限访问,这就涉及到了保护问题。保护相关的bit是存放在TLB的。

此外对于不同的进程,相同的地址非常可能对应不同的物理地址,不同的虚拟地址可能对应同样的物理地址。比如一个进程,通过它的虚拟地址改写了Cache的一部分,另外一个进程使用不同的虚拟地址,读了Cache的另外一部分。但是Cache的这两部分对应的都是同一个物理地址,那岂不是出错了?

 

上面的方法称为VIVT,基本没有处理器使用了。最常见的一级Cache普遍采用VIPT。我们去检索Cache用的仍然是虚拟地址的Index,但是我们需要用物理地址的TAG去做校验。物理地址具有唯一性,如果是两个不同的虚拟地址映射到同一物理地址,VIVT是会出问题的,但是VIPT不会。我们看一下为什么。

对于两个不同的虚拟地址,我们通过TLB翻译以后,其获得的物理地址一样(我们假设其映射到同样的物理地址)。因此其TAG也一样,但是我们访问Cache用的是虚拟地址的Index,也就意味着其访问到的是Cache的两块。所以理论上其至少有一个TAG是对不上的,对不上的话就需要从下一级访问Cache。而不是像VIVT那样使用Cache的两个不同部分,使用不同的两个部分,Cache的数据是可能不一样的,对应于同一物理地址,数据居然不一样,这显然不对。

 

我们看一下VIPT的局限,如下所示。

 

VIPT是L1 Cache普遍采用的方式,L2 Cache及以后的Cache,一般采用PIPT,因为此时速度就没那么重要了,设计简单,MISS率低更为重要。

 

最后我们对虚拟内存做一个总结。

 
 
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
用户名: 验证码: 点击我更换图片