searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

CPU缓存简介

2023-06-29 05:47:55
47
0

1. 缓存简介

1.1 缓存层次管理

      CPU Cache 通常会分为 L1、L2、L3 三层,其中 L1 Cache 通常分成「数据缓存」和「指令缓存」,L1是距离 CPU 最近的,因此它⽐ L2、L3 的读写速度都快、存储空间都⼩。可以通过如下命令查看CPU缓存层次:

    1. # lscpu | grep cache
    2. L1d cache:             32K
    3. L1i cache:             32K
    4. L2 cache:              4096K
    5. L3 cache:              16384K

图1:存储层次图

寄存器的访问速度⾮常快,⼀般要求在半个 CPU 时钟周期内完成读写。

  •  L1 ⾼速缓存通常分成指令缓存和数据缓存,2-4个时钟周期; 一级缓存越大,CPU的运行效率越高,但受到CPU内部结构的限制,一级缓存的容量都很小。指令缓存与数据缓存的区别只会在L1存在。
  • L2 和L1一样也是每个core独有的,只是离cpu更远,10-20个时钟周期; 主要就是做一级缓存和内存之间数据临时交换的地方用。L2缓存是同时存储指令和数据的。
  • L3是多个core共享的,20-60个时钟周期内存速度⼤概在200~300 个时钟周期之间。

 

1.2 缓存工作方式

整个缓存空间被分成了N个line,被称为缓存行,缓存行是cache和内存交换数据的最小单位。缓存行大小可有如下命令读取:

# cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size64

一个缓存行包含图2中的基本信息:

图2:缓存行信息

图中元素简介如下:

  • block:存放内存在cache中缓存的数据。
  • tag:存储该cache line 对应的内存块地址。
  • valid:cache line中的数据是否有效。

2. 缓存的映射方式

内存空间远大于cache空间,因此内存中的数据搬到cache中时,会存在一个多对一的映射。常见的缓存的映射方式包括如下三种:

Full-associative Cache(全关联Cache)

将内存也看成按照line的方式存储。在全关联cache中,内存中每个line能够被映射到cache中的任意一个cache line。

图3:全关联示意图

全关联,判断数据是否在cache中时,它需要将地址和所有cache line的tag匹配。

Direct-mapped Cache(直接映射cache)

将内存按照cache的大小的分成了N个page,每个page和cache大小相同。page中的line0只能映射到cache中的line0,以此类推。

图4:直接映射示意图

优点:随便给一个地址,通过地址就知道它位于哪个cache line中,tag记录page索引。

缺点:考虑执行如下一段代码:

for (i=0;i<10;i++){

   c+=a[i]+b[i]}

图5:cache miss示意图

当数组a和b中的元素都映射到line0时,可能发生严重的cache miss

Set-associate Cache(组关联Cache)

将cache分成了很多个way,每个way是完全相同的结构。Direct-mapped Cache等同于1-way Set associate Cache

在相同的cache容量下,cache way越多,则每个way的容量越小。way增加会减少cache miss,但way容量减少,又会增加cache miss。并且,way越多,cache的硬件结构越复杂,功耗也越高。不同处理器会根据应用设定最佳的way数和way的容量。

图6:组关联示意图

3. 缓存一致性

3.1 一致性底层操作

为了保证cache的一致性,处理器提供了两个保证cache一致性的底层操作:write invalidate和write update。

write invalidate:当一个core修改了一份数据,其它core上如果有该数据的缓存,则置为无效状态(invalid);该操作实现简单,尤其是其它core用不到该数据的时候,比较高效。缺点:valid标志对应一个cache line,会导致cache line中其它有效数据也无效。

图7:write invalidate示意图

write update:当一个core修改了一份数据,其他地方如果有该数据的缓存,就都更新到最新值。会导致大量的数据更新操作,不过只用更新修改的数据。

图8:write update示意图

 

3.2 MESI协议

3.2.1 MESI协议简介

每个cache中每个cache line有两个标志:dirty(是否被修改,内存和cache一致性)和vaild(是否有效)。

MESI是Modified、Exclusive、Shared、Invalid这四个单词的首字母。这4个字母分别代表4种状态:

状态

描述

监听任务

Modified

(修改)

该缓存行有效,但是该缓存数据已经被当前核心修改,此时和DRAM中数据不一致。我们将其置为M,其他的核中缓存行都会置为I。

监听总线上所有对该缓存行写回DRAM的操作(不希望别人写入),需要将该操作延迟到自己将缓存行写回到主存后变成S状态。

Exclusive(互斥)

该缓存行有效,数据和RAM的数据一致,数据只存在当前内核工作内存中,只有他在使用是独占的。

监听总线上所有从DRAM读取该缓存行的操作,一旦有读的,需要将状态置为S状态。

Shared

(共享)

该缓存行有效,不过当前缓存行在多个核中都有,并且大家以及DRAM中的都一样。

监听其他的缓存中将该缓存置为I或者为E的事件,将状态置为I状态。

Invalid

(无效)

表明该缓存行无效,如果想要获取数据的话,就去DRAM中加载最新的。

不需要监听。

         图9:Exclusive示意图

图10:Share示意图

            图11:Modified和Invalid示意图

在MESI协议中,每个cache的cache控制器不仅知道自己的读写操作,而且也监听其它cache的读写操作。每个cache line的状态根据本core和其它core的读写操作在4个状态间转移。

当有一个核去修改了自己的缓存行,需要同步到其他的核并更新他们的状态。所以说在MESI中每个cache控制器,不仅需要知道自己的操作,还会监听其他的cache的操作。

我们把CPU各个内核对缓存的操作可以总结为4种操作:

  • local read:CPU内核读取自己的本地缓存
  • local write:CPU内核写入自己的本地缓存
  • remote read:其他的CPU内核读取了DRAM中当前内核的缓存行
  • remote write:其他的CPU内核写入了DRAM中当前内核的缓存行

CPU内核中的缓存会监听这些事件来修改自己缓存的缓存行中的Flag标志位。然后通过该标志位来决定CPU如何处理这个缓存数据

3.2.2 MESI状态转移图

MESI状态转移图如下所示。

 图12:MESI状态转移图

 

我们这里用CPU的两个核CA(coreA)和CB(coreB)以及缓存行数据 X 来解释下上面图片的转变,当CA中存在X:

状态是M(修改):此时只有CA内部有X,并且X和RAM的X值是不一致的。

事件

行为

下一个状态

local read

直接从CA的cache中读,状态不发生改变

M

local write

直接修改CA的cache数据,状态不变

M

remote read

CB需要最新数据,将CA的X值写回到RAM中
CB从RAM再读取X,CA和CB的缓存行标志为设置为S

S

remote write

先将CA的X值写回到RAM中
CB读取X并修改,CA的状态变为I,CB的状态变为M

I

 

状态是S(共享):此时CA和CB都有X,且和RAM的值都一致。

事件

行为

下一个状态

local read

直接从CA的cache中读,状态不发生改变

S

local write

CA直接修改cache,状态变为M。CB变为I

M

remote read

CB读的和CA的一样的数据,状态不变

S

remote write

CB对应成了上面CA的local write,CB的cache变为M,CA的变为I

I

 

状态是E(独占):此时只有CA有X,且X和RAM的X值一致(值一致代表着不需要写回RAM)

事件

行为

下一个状态

local read

直接从CA的cache中读,状态不发生改变

E

local write

直接修改CA的cache,状态变为M

M

remote read

CB发送读事件,CA和CB需要共享X,所以状态都变为S

S

remote write

CA将X置为I

I

 

状态是I(失效):需要依据CB是否有X,以及响应的状态来决定操作。

事件

行为

下一个状态

local read

如果CB没有X,则CA读取X,状态为E
如果CB有X,状态为M,则CB需要写回到RAM,然后CA读取,状态变为S
如果CB有X,状态为S或者E,那么CA直接读,CA和CB都变为S

E or S

local write

CA需要从RAM拉取数据
如果CB没有X,那么CA就直接拉取,修改后设置为M
如果CB有X,状态为M,那么CB需要先写回RAM,然后CA读取最新到cache,修改并设置为M,CB变为I
如果CB有X,状态为S或者E,那么CA读取并设置为M,CB为I

M

remote read

已经失效,只和自己读写有关,和其他的读写无关,状态不变。

I

remote write

已经失效,只和自己读写有关,和其他的读写无关,状态不变。

I


主要抓住几个规则:

  • 当有核心读的时候,需要关注其他核心的状态是否有M的,有M的必须要先让M的写入到RAM,再读最新数据。即:当read操作需要到RAM中取时,整个CPU层面不能有该缓存行状态为M的,有的必须让其先写回到RAM中。
  • 当有核心写的时候,涉及会比较多,但是核心就是:写的时候CPU层面不能有任何其他核心处于M状态,有就需要将其先写回RAM,拿到最新的数据修改,修改完成,该核心以外的核都变为失效。
  • 所有的这些操作都是为保证:任何核心的修改不能被覆盖!任何核心的读取,都需要拿到当前CPU缓存层面最新的值!

 

0条评论
0 / 1000
陈****豪
3文章数
0粉丝数
陈****豪
3 文章 | 0 粉丝
陈****豪
3文章数
0粉丝数
陈****豪
3 文章 | 0 粉丝
原创

CPU缓存简介

2023-06-29 05:47:55
47
0

1. 缓存简介

1.1 缓存层次管理

      CPU Cache 通常会分为 L1、L2、L3 三层,其中 L1 Cache 通常分成「数据缓存」和「指令缓存」,L1是距离 CPU 最近的,因此它⽐ L2、L3 的读写速度都快、存储空间都⼩。可以通过如下命令查看CPU缓存层次:

    1. # lscpu | grep cache
    2. L1d cache:             32K
    3. L1i cache:             32K
    4. L2 cache:              4096K
    5. L3 cache:              16384K

图1:存储层次图

寄存器的访问速度⾮常快,⼀般要求在半个 CPU 时钟周期内完成读写。

  •  L1 ⾼速缓存通常分成指令缓存和数据缓存,2-4个时钟周期; 一级缓存越大,CPU的运行效率越高,但受到CPU内部结构的限制,一级缓存的容量都很小。指令缓存与数据缓存的区别只会在L1存在。
  • L2 和L1一样也是每个core独有的,只是离cpu更远,10-20个时钟周期; 主要就是做一级缓存和内存之间数据临时交换的地方用。L2缓存是同时存储指令和数据的。
  • L3是多个core共享的,20-60个时钟周期内存速度⼤概在200~300 个时钟周期之间。

 

1.2 缓存工作方式

整个缓存空间被分成了N个line,被称为缓存行,缓存行是cache和内存交换数据的最小单位。缓存行大小可有如下命令读取:

# cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size64

一个缓存行包含图2中的基本信息:

图2:缓存行信息

图中元素简介如下:

  • block:存放内存在cache中缓存的数据。
  • tag:存储该cache line 对应的内存块地址。
  • valid:cache line中的数据是否有效。

2. 缓存的映射方式

内存空间远大于cache空间,因此内存中的数据搬到cache中时,会存在一个多对一的映射。常见的缓存的映射方式包括如下三种:

Full-associative Cache(全关联Cache)

将内存也看成按照line的方式存储。在全关联cache中,内存中每个line能够被映射到cache中的任意一个cache line。

图3:全关联示意图

全关联,判断数据是否在cache中时,它需要将地址和所有cache line的tag匹配。

Direct-mapped Cache(直接映射cache)

将内存按照cache的大小的分成了N个page,每个page和cache大小相同。page中的line0只能映射到cache中的line0,以此类推。

图4:直接映射示意图

优点:随便给一个地址,通过地址就知道它位于哪个cache line中,tag记录page索引。

缺点:考虑执行如下一段代码:

for (i=0;i<10;i++){

   c+=a[i]+b[i]}

图5:cache miss示意图

当数组a和b中的元素都映射到line0时,可能发生严重的cache miss

Set-associate Cache(组关联Cache)

将cache分成了很多个way,每个way是完全相同的结构。Direct-mapped Cache等同于1-way Set associate Cache

在相同的cache容量下,cache way越多,则每个way的容量越小。way增加会减少cache miss,但way容量减少,又会增加cache miss。并且,way越多,cache的硬件结构越复杂,功耗也越高。不同处理器会根据应用设定最佳的way数和way的容量。

图6:组关联示意图

3. 缓存一致性

3.1 一致性底层操作

为了保证cache的一致性,处理器提供了两个保证cache一致性的底层操作:write invalidate和write update。

write invalidate:当一个core修改了一份数据,其它core上如果有该数据的缓存,则置为无效状态(invalid);该操作实现简单,尤其是其它core用不到该数据的时候,比较高效。缺点:valid标志对应一个cache line,会导致cache line中其它有效数据也无效。

图7:write invalidate示意图

write update:当一个core修改了一份数据,其他地方如果有该数据的缓存,就都更新到最新值。会导致大量的数据更新操作,不过只用更新修改的数据。

图8:write update示意图

 

3.2 MESI协议

3.2.1 MESI协议简介

每个cache中每个cache line有两个标志:dirty(是否被修改,内存和cache一致性)和vaild(是否有效)。

MESI是Modified、Exclusive、Shared、Invalid这四个单词的首字母。这4个字母分别代表4种状态:

状态

描述

监听任务

Modified

(修改)

该缓存行有效,但是该缓存数据已经被当前核心修改,此时和DRAM中数据不一致。我们将其置为M,其他的核中缓存行都会置为I。

监听总线上所有对该缓存行写回DRAM的操作(不希望别人写入),需要将该操作延迟到自己将缓存行写回到主存后变成S状态。

Exclusive(互斥)

该缓存行有效,数据和RAM的数据一致,数据只存在当前内核工作内存中,只有他在使用是独占的。

监听总线上所有从DRAM读取该缓存行的操作,一旦有读的,需要将状态置为S状态。

Shared

(共享)

该缓存行有效,不过当前缓存行在多个核中都有,并且大家以及DRAM中的都一样。

监听其他的缓存中将该缓存置为I或者为E的事件,将状态置为I状态。

Invalid

(无效)

表明该缓存行无效,如果想要获取数据的话,就去DRAM中加载最新的。

不需要监听。

         图9:Exclusive示意图

图10:Share示意图

            图11:Modified和Invalid示意图

在MESI协议中,每个cache的cache控制器不仅知道自己的读写操作,而且也监听其它cache的读写操作。每个cache line的状态根据本core和其它core的读写操作在4个状态间转移。

当有一个核去修改了自己的缓存行,需要同步到其他的核并更新他们的状态。所以说在MESI中每个cache控制器,不仅需要知道自己的操作,还会监听其他的cache的操作。

我们把CPU各个内核对缓存的操作可以总结为4种操作:

  • local read:CPU内核读取自己的本地缓存
  • local write:CPU内核写入自己的本地缓存
  • remote read:其他的CPU内核读取了DRAM中当前内核的缓存行
  • remote write:其他的CPU内核写入了DRAM中当前内核的缓存行

CPU内核中的缓存会监听这些事件来修改自己缓存的缓存行中的Flag标志位。然后通过该标志位来决定CPU如何处理这个缓存数据

3.2.2 MESI状态转移图

MESI状态转移图如下所示。

 图12:MESI状态转移图

 

我们这里用CPU的两个核CA(coreA)和CB(coreB)以及缓存行数据 X 来解释下上面图片的转变,当CA中存在X:

状态是M(修改):此时只有CA内部有X,并且X和RAM的X值是不一致的。

事件

行为

下一个状态

local read

直接从CA的cache中读,状态不发生改变

M

local write

直接修改CA的cache数据,状态不变

M

remote read

CB需要最新数据,将CA的X值写回到RAM中
CB从RAM再读取X,CA和CB的缓存行标志为设置为S

S

remote write

先将CA的X值写回到RAM中
CB读取X并修改,CA的状态变为I,CB的状态变为M

I

 

状态是S(共享):此时CA和CB都有X,且和RAM的值都一致。

事件

行为

下一个状态

local read

直接从CA的cache中读,状态不发生改变

S

local write

CA直接修改cache,状态变为M。CB变为I

M

remote read

CB读的和CA的一样的数据,状态不变

S

remote write

CB对应成了上面CA的local write,CB的cache变为M,CA的变为I

I

 

状态是E(独占):此时只有CA有X,且X和RAM的X值一致(值一致代表着不需要写回RAM)

事件

行为

下一个状态

local read

直接从CA的cache中读,状态不发生改变

E

local write

直接修改CA的cache,状态变为M

M

remote read

CB发送读事件,CA和CB需要共享X,所以状态都变为S

S

remote write

CA将X置为I

I

 

状态是I(失效):需要依据CB是否有X,以及响应的状态来决定操作。

事件

行为

下一个状态

local read

如果CB没有X,则CA读取X,状态为E
如果CB有X,状态为M,则CB需要写回到RAM,然后CA读取,状态变为S
如果CB有X,状态为S或者E,那么CA直接读,CA和CB都变为S

E or S

local write

CA需要从RAM拉取数据
如果CB没有X,那么CA就直接拉取,修改后设置为M
如果CB有X,状态为M,那么CB需要先写回RAM,然后CA读取最新到cache,修改并设置为M,CB变为I
如果CB有X,状态为S或者E,那么CA读取并设置为M,CB为I

M

remote read

已经失效,只和自己读写有关,和其他的读写无关,状态不变。

I

remote write

已经失效,只和自己读写有关,和其他的读写无关,状态不变。

I


主要抓住几个规则:

  • 当有核心读的时候,需要关注其他核心的状态是否有M的,有M的必须要先让M的写入到RAM,再读最新数据。即:当read操作需要到RAM中取时,整个CPU层面不能有该缓存行状态为M的,有的必须让其先写回到RAM中。
  • 当有核心写的时候,涉及会比较多,但是核心就是:写的时候CPU层面不能有任何其他核心处于M状态,有就需要将其先写回RAM,拿到最新的数据修改,修改完成,该核心以外的核都变为失效。
  • 所有的这些操作都是为保证:任何核心的修改不能被覆盖!任何核心的读取,都需要拿到当前CPU缓存层面最新的值!

 

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0