0%

cache 整理

l1/l2 cache 结构

image-20240416112815996

cache控制器是如何判断数据是否在cache中命中呢?所以cache肯定是只能缓存主存中极小一部分数据。我们如何根据地址在有限大小的cache中查找数据呢?现在硬件采取的做法是对地址进行散列(可以理解成地址取模操作)

image-20240416112819790

我们一共有8行cache line,cache line大小是8 Bytes。所以我们可以利用地址低3 bits(如上图地址蓝色部分)用来寻址8 bytes中某一字节,我们称这部分bit组合为offset。
同理,8行cache line,为了覆盖所有行。我们需要3 bits(如上图地址黄色部分)查找某一行,这部分地址部分称之为index

tag array和data array一一对应。每一个cache line都对应唯一一个tag,tag中保存的是整个地址位宽去掉index和offset使用的bit剩余部分(如上图地址绿色部分)。tag、index和offset三者组合就可以唯一确定一个地址了。因此,当我们根据地址中index位找到cache line后,取出当前cache line对应的tag,然后和地址中的tag进行比较,如果相等,这说明cache命中。如果不相等,说明当前cache line存储的是其他地址的数据,这就是cache缺失。

Directory Mapped

image-20240416112823674

0x00、0x40 地址中index部分是一样的。因此,这2个地址对应的cache line是同一个。所以,当我们访问0x00地址时,cache会缺失
, 然后数据会从主存中加载到cache中第0行cache line;
当我们访问0x40地址时,依然索引到cache中第0行cache line,由于此时cache line中存储的是地址0x00地址对应的数据,所以此时依然会cache缺失。然后从主存中加载0x40地址数据到第一行cache line中

访问0x40地址时,就会把0x00地址缓存的数据替换。这种现象叫做cache颠簸(cache thrashing)

四路组相联

image-20240416112827437

降低了cache颠簸的频率

VIPT

我们可以使用虚拟地址对应的index位查找cache,与此同时(硬件上同时进行)将虚拟地址发到MMU转换成物理地址。当MMU转换完成,同时cache控制器也查找完成,此时比较cacheline对应的tag和物理地址tag域,以此判断是否命中cache。我们称这种高速缓存为VIPT(Virtually Indexed Physically Tagged)

image-20240416112831504

write back

当CPU执行store指令并在cache命中时,我们只更新cache中的数据。并且每个cache line中会有一个bit位记录数据是否被修改过,称之为dirty bit (D)
主存中的数据可能是未修改的数据,而修改的数据躺在cache中。cache和主存的数据可能不一致

self-modify l1 icache

self-modify

image-20240416112835829

cache coherence

缓存一致性协议 MESI

M : modified
E : exclusive
S : shared
I : invalid

协议在每一个 cache line 中维护一个两位的状态 “tag” ,这个 “tag” 在 cache line 的物理地址或者数据后

image-20240416112839686

MESI 演示

image-20240416112847184

image-20240416112851863

Store Buffer / Invalidate Queue

MESI解决了缓存一致性问题,但是它有一个性能弱点:
处理器执行写操作时,必须等待其他处理器将其高速缓存中的相应副本数据删除, 并接收到这些处理器所回复的Invalidate Acknowledge/Read Response消息之后才能将数据写入高速缓存。

image-20240416112855625

为了规避和减少这种等待造成的写操作的延迟(Latency), 引入了Store Buffer和Invalidate Queue。

Store Buffer是处理器内部的一个容量比高速缓存器还小的私有高速存储部件,每个处理器都有其写存储器,并且一个处理器无法读取另外一个处理器上的Store Buffer中的内容。

image-20240416112858588

处理器可以并不等待其他处理器返回Invalidate Acknowledge/Read Response消息而是继续执行其他指令。
当一个处理器接收到其他处理器所回复的针对同一个缓存行的所有Invalidate Acknowledge消息的时候,该处理器会将Store Buffer中针对相应地址的写操作的结果写入相应的缓存行,此时写操作对于其他处理器来说才算完成。

有了Store Buffer后,一个处理器在更新了一个变量之后,马上又读取了该变量的值,但是由于该处理器先前对该变量的更新结果可能仍然还停留在Store Buffer中,因此该变量相应的内存地址所对应的缓存行中仍存储着该变量的旧值。
因此处理器在执行读操作的时候会根据内存地址查询Store Buffer。

  • 如果Store Buffer存在该数据,那么会直接将该数据作为结果返回。
  • 如果不存在,处理器会从高速缓存中读取数据。

这种处理器直接从Store Buffer中读取数据来实现内存读操作的技术被称为存储转发(Store Buffer)

Invalidate Queue

引入Invalidate Queue之后,处理器在接收到Invalidate消息之后并不删除消息中指定地址的副本数据,而是将消息存入 Invalidate Queue之后就回复Invalidate Acknowledge消息,从而减少了执行写操作的处理器所需的等待问题。

image-20240416112903648

可见性

可见性即一个线程对共享变量值的修改,能够及时被其他线程看到。 但是由于Store Buffer和Invalidate Queue的存在,使得数据可能不被其他线程及时看到。

  1. 现代处理器在一些特定条件下(比如Store Buffer满,I/O指令被执行)会将Store Buffer中的内容写入高速缓存,但是这种写入并不一定是及时的,也就是说Store Buffer中的数据可能并没有刷新到高速缓存。
  2. 处理器在执行内存读取操作的时候如果没有根据Invalidate Queue中的内容将该处理器上的高速缓存中的相关副本数据删除,那么也可能导致该处理器读到的数据是过时的数据。

为了解决上面的两个问题,首先要使写线程对共享变量所做的更新能够及时到达高速缓存,从而使该更新对其他处理器是同步的;
其次,读线程所在的处理器要将其Invalidate Queue中的内容进行处理,保证读线程读到的数据是新的
。而底层系统会借助一类被称为内存屏障的特殊指令。

  • Store Barrier:可以使执行该指令的处理器冲刷其Store Buffer中的数据到缓存,从而保证某线程对共享变量所做的更新对读线程是可见的。
  • Load Barrier:会根据Invalidate Queue中内容所指定的内存地址,将处理器上高速缓存中的状态标记为I,从而使该处理器后续执行这些地址的读操作时必须发送Read消息,从而保证了处理器读到的数据是新的。

CM - cluster 内部

Setting the Cache Coherency Attributes for Default Memory Transfers 405
CCA_Override_Value 覆盖默认的L2 配置.

cluster 间同步

image-20240416112907383