Skip to content

【计组】Cache(上)-工作原理与映射方式

Computer Architecture Cache AI Assisted
Contents

一、基本概念

1. Memory hierarchy

CPU跑在GHz级别,DDR(DRAM)访问一次要几十到上百ns,中间差了上百个cycle。Cache用SRAM做在CPU片内(on-chip),存放最常访问的数据,CPU访存时先查cache。

层级位置实现Latency(量级)
L1on-chip,每个core privateSRAM几个cycle
L2on-chip,常见为每个core privateSRAM十几个cycle
L3on-chip,所有core sharedSRAM几十个cycle
DDRoff-chipDRAM上百个cycle

越靠近CPU越快、越小、每字节越贵。

2. Hit与Miss

  • hit:要的数据在cache里,直接从cache取
  • miss:不在cache里,要去下一级DDR取。下文为了简单,下一级统一写成DDR

衡量cache好坏常用AMAT(average memory access time):

AMAT = hit time + miss rate × miss penalty

  • miss penalty:从下一级取回一整条cacheline的时间 = access time(等第一个数据回来,看latency)+ transfer time(把整条搬过来,看两级之间的bandwidth)

多级cache:某一级的miss penalty,就是去它下一级取数据的平均时间,而这个时间本身又是一个AMAT,所以一层套一层:

AMAT = Thit(L1) + miss%(L1) × [ Thit(L2) + miss%(L2) × ( Thit(L3) + miss%(L3) × T(memory) ) ]

  • Thit(Lx):Lx的hit time;T(memory):访问DDR的时间
  • miss%(Lx):到达Lx的那些访问里有多少miss(local miss rate),不是占全部访问的比例
Example

假设

  • L1的miss rate 10%、hit time 1 cycle;
  • L2 5%、10 cycle;
  • L3 1%、20 cycle;DDR 300 cycle。

从最里层往外算:

结构计算AMAT
只有L11 + 0.1 × 30031
L1 + L2L2:10 + 0.05 × 300 = 25 → L1:1 + 0.1 × 253.5
L1 + L2 + L3L3:20 + 0.01 × 300 = 23 → L2:10 + 0.05 × 23 = 11.15 → L1:1 + 0.1 × 11.152.115

加L2的收益最大:L1 miss的那10%访问,不用每次都等DDR的300拍。

3. Cacheline与Offset

  • cacheline:cache和DDR(下一级)之间搬数据的最小单位,也就是两者之间的访问粒度。

    • CPU的访存请求先交给cache(不可缓存的区域除外);miss时是cache去DDR搬一整行cacheline回来,不是只搬要的那个字节
  • offset:CPU和cache之间的访问粒度是byte或word(由load / store指令决定),用offset指出要的是这一行cacheline里从哪个字节开始

  • offset位数 = log2(cacheline字节数),一行里有2^offset位数个字节。例:64字节的行要6位offset

4. Locality与Prefetch

怎么知道哪些数据「常访问」?靠程序的locality:

  • temporal locality:某一时刻访问了地址A,不久之后很可能再访问A。例:循环里反复用到的变量
  • spatial locality:访问了地址A,很可能接着访问A附近的地址。例:顺序遍历数组

一次搬一整条cacheline,本身就在利用spatial locality。

  • prefetch更进一步:取地址A时,顺便把A附近的数据(比如下一条cacheline)也取进cache。

5. Cache里放的是什么

内存按cacheline的大小切成一块一块(block)。cache只有很少几条cacheline,每条一次只能放一块。放哪一块,不是CPU挑的,而是由块地址里的Index位决定,所以很多块要共用同一条cacheline(详见第二节)。

Example

内存有16块,块号0~F;cache有4条cacheline,用块号的低2位决定放哪条(direct mapped):

cacheline能放的块
0块0、块4、块8、块C
1块1、块5、块9、块D
2块2、块6、块A、块E
3块3、块7、块B、块F
  • cacheline 0同一时刻只能放这4块里的一块,其余3块都不在cache里

🟠所以每条cacheline除了数据,还要存tag,记录「现在放的是哪一块」;再存valid bit,记录「这条里有没有数据」

CPU访问的是内存地址,不是cache里的位置

CPU读或写,给出的都是内存地址。cache用地址里的Index找到对应的那条cacheline,再比较tag(并看valid):这个地址所在的块,现在是不是放在这条cacheline里。

  • 在:hit。读就取数据,写就直接写进这条cacheline
  • 不在:miss。要先把这一块放进cache(或者像write no-allocate那样,绕过cache直接写DDR)

所以写操作也一样:对应的块在cache里才算hit。

二、Cache的寻址与组织方式

1. Set和Way

  • set:地址先决定放进哪一个set
  • way:一个set里有几条cacheline
  • X-way set associative = 每个set有X条cacheline
Example

下图一共8条cacheline,分成2个set、每个set有4条,是4-way set associative(2 sets),不是2-way:

set0:way0、way1、way2、way3

set1:way0、way1、way2、way3

三种cache组织结构:

  1. Direct mapped:1-way。每个地址只能放进唯一的一条cacheline
  2. X-way set associative:地址决定放进哪个set,set内X条随便放
  3. Fully associative:只有1个set,way数等于cacheline总数(way数最大)。任何地址都能放进任何一条
Example

用同一个小例子对比这三种结构:内存32字节,地址ADDR[4:0]共5位,每个地址存1字节DATA[7:0];cache共4条cacheline,每条1字节,所以没有offset。Index和Tag是地址的两部分,下一小节讲。

Index分别为2bit、1bit、0bit(fully associative没有Index)。

Mappingset × wayIndexTag
Direct mapped4 × 1ADDR[1:0]ADDR[4:2]
2-way set associative2 × 2ADDR[0]ADDR[4:1]
Fully associative1 × 4无ADDR[4:0]

set associative让一个地址在cache里有多个可放的位置(X-way就有X个),两个常用地址落到同一个set时可以同时留在cache里,conflict miss变少、命中率提高;代价是每次访问要并行比较X个tag,再用X选1的mux选出命中那一路,面积和hit time都比direct mapped大。

2. 地址划分

地址 = Tag | Index | Offset(从高位到低位)

  • Offset:log2(cacheline字节数)位,选行里的哪个字节
  • Index:log2(set数)位,选哪一个set(大多数情况下Index就是set号)
  • Tag:剩下的高位。每条cacheline旁边存一份tag,查找时和地址里的Tag比较,相同才可能hit
Example

1KB的direct mapped cache,cacheline 32字节,地址32位:

  • Offset:log2(32) = 5位,ADDR[4:0]
  • Index:1KB ÷ 32字节 = 32条cacheline,direct mapped每个set只有1条,所以有32个set,log2(32) = 5位,ADDR[9:5]
  • Tag:剩下的ADDR[31:10],22位

图源:UIUC ECE 411课件

访问地址0x44:先把0x44转成二进制,看第[4:0]位决定是cacheline里哪个offset,也就是第几个字节。然后看地址的[9:5]决定是哪个set。

0x44 = 0…0_00010_00100(Tag_Index_Offset)

Index = 00010 —— set 2

Offset = 00100 —— 这条cacheline的第4个字节(从0数)

Tag = 0…0 —— 拿去和set 2里存的tag比较

way数变化时:总容量和cacheline大小不变,way翻倍 → set数减半 → Index少1位、Tag多1位(Index让1位给Tag);Offset不变。

Example

32KB的cache,cacheline 32字节,地址32位:一共1024条cacheline,Offset都是5位。

Direct mapped2-way4-way
set数1024512256
Index10位9位8位
Tag17位18位19位

每条cacheline除了数据,还存tag和valid bit(见第一章第5小节),write-back cache还要存dirty bit(见下篇)。valid上电时全为0,所以tag相同且valid = 1才算hit。

一次查找的过程(以2-way为例): CPU访问的是内存地址,cache用Index找到cacheline,再比较tag和valid,这叫cache lookup

3. 三种Mapping对比

Direct mappedX-way set associativeFully associative
一个地址能放的位置1处set内X处任意一处
每次查找比较几个tag1X全部(CAM)
conflict miss多少没有
硬件开销、hit time最小中等最大
需要replacement policy不需要需要需要
  • CAM(content-addressable memory):给它一个内容(tag),它直接告诉你存在哪一项。做法是每一项配一个比较器,所有项同时比较,所以fully associative硬件开销最大,只用在很小的结构上(如TLB)

Miss的分类(3C)

miss原因对策
compulsory miss第一次访问,cache里本来就没有增大block size、prefetch
capacity misscache总容量装不下程序正在用的数据增大cache
conflict miss总容量够,但几块挤进同一个set,互相踢掉增加associativity,或增大cache
  • 分辨capacity和conflict:换成同样大小的fully associative再跑一遍,还miss就是capacity,不miss就是conflict(fully associative没有「挤在同一个set」的问题)
  • 对策有代价:cache越大、way越多,hit time越长;block size太大,miss penalty和miss rate反而上升。怎么取舍见Cache性能优化
Example

4条cacheline的direct mapped,依次访问块0 → 块4 → 块0 → 块4。块0和块4都只能放cacheline 0,cacheline 1~3一直空着(容量够):

  • 第1、2次访问:compulsory miss,两块都是第一次来
  • 第3、4次访问:conflict miss,块0、块4互相把对方踢掉;换成4条的fully associative,两块能同时放下,这两次都会hit

4. Tag array和data array

第2小节的cache lookup,硬件上由两块独立的存储完成,都按set组织、每个way一列:

  • tag array:存tag。读出来和地址里的Tag比较,得到每个way的hit
  • data array:存数据。读出来经过mux,由hit和Offset选出要的那几个字节

图源:UIUC ECE 411课件

两块分开放,tag array小、读得快,hit / miss能先算出来。两块怎么配合着读(并行读还是串行读),见Cache性能优化。

下篇:Cache(下):读写策略、替换算法与一致性问题

参考

Discussion

Loading comments…

Checking sign-in…