Computer Architecture 笔记 —— Cache
Posted on 2021-11-20 19:21 in IC
Note
Cache 是一个非常广泛、有深度的话题,无数的大牛在此道沉浸数十载,创造出了各种各样的优化技巧和实现技术,一篇短短的博文显然无法做到面面俱到,作为学习笔记,只争取说明白最基础的 Cache 知识。
为什么需要 Cache?
面临的问题
众所周知,处理器的集成度一直在按照摩尔定律逐渐提高,在 20 世纪的后 20 年内,core 的时钟频率也基本按照每 18 个月翻一番的速度增长,但是 DRAM 的时钟频率增长速度却每年只有 7%,所以两者的速度之间就形成了一个 剪刀差
:随着时间的推移,这个差距会越来越大。即使在现在这个多核时代,core 的频率不再怎么提升了,这个差距仍然在扩大,因为核数量增加对内存的带宽需求也会相应增加。
这个问题是码农和硅农之间的矛盾:码农希望存储器的容量尽可能大的同时速度足够快,而实际上硅农受限于半导体技术,速度快的 SRAM 容量无法做得很大,成本太高;而容量大的 DRAM 速度无法做得很快。举个例子,假设一个 core 的频率为 2 GHz 的 4-way 超标量处理器,它直接访问 latency = 100ns 的 DRAM,那么访问一次 DRAM 的时间内处理器可以执行多少条指令呢?
这显然是不可接受的。
解决方案
按照传统的冯 · 诺依曼结构,指令和数据都在内存中,CPU 只负责处理,内存和 CPU 关系就像是仓库和工厂,工厂加工的原料和生产出来的产品都要放在仓库中。但是摩尔定律和 “ 剪刀差 ” 导致工厂和仓库之间的运输能力成为整个系统的瓶颈,最简单直观的解决办法就是在工厂里面做一个小仓库,对应在处理器中就是存储器层次 Memory Hierarchy
。
存储层次 | 大小 | 访问 latency | 数据调度 | 调度单位 | 实现技术 |
---|---|---|---|---|---|
register file | < 1 KB | 0.25 ~ 0.5 ns | 编译器 | Word | CMOS 寄存器堆 |
cache | < 16 MB | 0.5 ~ 25 ns | 硬件 | block | CMOS SRAM |
memory | < 16 GB | 80 ~ 250 ns | 操作系统 | page | CMOS DRAM |
I/O device | > 100 GB | 5ms | 人工 | file | disk |
cache 能缓解问题的原因在于程序具有局部性原理:
时间局部性
:一个数据被访问之后,短期内很大概率会被再次访问空间局部性
:一个数据被访问之后,短期内很大概率会访问相邻数据算法局部性
:程序时空间隔都很大地重复访问数据,常见于图像处理等算法
其中常规的 prefetch 算法无法识别并利用算法局部性,只能靠程序员观察数据访问规律,并重写算法,依靠 programmer/ccompiler 插入 software prefetch 指令。
Cache 的出现可以说是一种无奈的妥协。如果 DRAM 的速度足够快,或者 SRAM 的容量可以做到足够大,我们的烦恼不复存在了,cache 也没有存在的必要了。但是在未来一段时间内,当今硅工艺不发生革命性变化的前提下,这是很难实现的一件事情,所以 cache 是有必要的。
Note
冯诺依曼结构中有个永恒的主题:如何喂饱饥饿的 CPU,即如何提供稳定的指令流和数据流:
- 指令流:分支预测
- 数据流:cache
经过多年发展,大概能达到半饱的程度:4 发射的结构,IPC = 2 就已经很不错了。
如何确定 Cache 规格?
cache 的规格可以总结为下面几个问题。
容量选择
Q:Cache 的总容量应该设置为多少合适?
A:没有标准答案,应该根据 应用需求
和 微架构
设计特点做出选择。
首先,cache 容量对 area 有最直接的影响,一般 cache 占用处理器 60% ~ 80% 的晶体管和 30% 以上的总面积,在某些处理器中甚至达到了 80% 的面积。所以确定 cache 容量时,首先要考虑的就是面积约束(即 area 和 money)。
其次,在满足面积约束的前提下,显然希望 cache 性能越高越好。根据 3C 模型,可以知道
- 增加容量的优点:可以降低 capacity miss,从而降低整体的 miss rate
- 增加容量的缺点:导致时序变差,导致 hit time 和 miss penality 变大
所以 cache 容量是一把双刃剑,并不是越大性能就越高。
目前的主流方案是多级 cache,不同级别的 cache 设计目标不同,所以容量规格也不同。因为其他级 cache 的出现,每一级 cache 的最佳规格、设计思路与单级 cache 方案完全不同:
- L1 离 core 最近,其目标是跟上 core 的速度,所以会选择小容量、低相联度的结构,牺牲一些 hit rate,尽量减小 latency,换取高 throughput 和低 hit time。它的容量和 block size 相比于单级 cache 来说都要小很多,以减小 miss penality
- L2 离 core 远一些,其目标则是低 miss rate,所以会选择大容量、高相联度的结构,牺牲一些频率、throughput 和 latency,换取更低的 miss rate。它的容量和 block size 都要比单级 cache 要大很多
Block 大小选择
Q:每个 block 的大小应该设置为多少?
A:应该根据 cache size
做出选择 , 一般为 4~8 Word,常见的组合有 cache size = 4KB,block size = 32B; cache size > 64KB, block size = 64B。
较大的 block 可以更好地利用空间局部性,所以可以降低 miss rate,但是当 block 占 cache 容量的比例大到一定程度时,因为 block 的数量变得很少,此时会有大量的冲突,数据在被再次访问前就已经被替换出去了,而且太大的 block 内部数据的空间局部性也会降低,所以会导致 miss rate 反而上升。
随着 block 的增大,miss rate 的改善逐渐降低,但是在不改变 memory 系统的前提下,miss penalty 会随着 block 的增大而增大,所以当 miss penalty 超过了 miss rate 的收益,cache 的性能就会变低。
block 的大小还依赖于下一级存储器的 latency 和 throughput:
- latency 和 throughput 越大,越应该使用大 block:因为每次 miss 可以取得更多的数据,但是 miss penality 增长很小(因为此时 miss penality 的主要成分是 latency,所以增大 block 额外传输数据的时间占比很小)
- latency 和 throughput 越小,越应该使用小 block:因为这种情况下增大 block 并不会节省多少时间(比如小块的 penlaty*2 和一个两倍大小 block 的 penalty 相同,此时显然选小 block 更好,还能减小 conflict miss)
Tip
较大 block 会导致较长的传输时间,虽然这部分时间很难优化,但是我们可以隐藏一些数据传输的时间,从而降低 miss penalty。实现这个效果的最简单的技术叫做 early restart
:一旦接收到需要的 word 就立即就开始重启流水线,而不是等到整个 block 都返回后才重启。许多处理器都在 I-cache 上使用这个技术,效果甚佳,这是因为大部分指令访问都具有连续性。这个技术对于 D-cache 来说效果就没那么好了,因为数据访问的预测性没那么好,在传输结束前请求另外一个 block 中 word 的概率很高,而此时前一次请求的数据传输还没有结束,所以仍然会导致处理器 stall。
还有一种更加复杂的机制叫做 requested word first
或者是 critical word first
,这种方案会重新组织 memory 的结构,使得被请求的 word 优先返回,然后按照顺序返回后续数据,最后反卷到 block 的开头部分。这种方法比 early restart 稍微快一点,但是会受到相同的限制。
映射方式
Q:应该如何组织 cache 的存储结构?
A:根据 cache size
三选一,有个一般性的规律:
2:1 cache rule of thumb:容量为 N、直接映射的 miss rate = 容量为 N/2、相联度为 2-way 的 miss rate。
cache 的工作方式和停车场非常类似,如果停车场(cache)中有可用的空车位(cache line),那么汽车(data)就可以停在该车位中;如果停车场已经没有空车位,那么就要先把某个车开出来(数据替换出去),然后才能把新来的车停进去。而在停车场找车时,如果停车场很大,而且所有的车都随机停,那么找车(查找数据)的速度就会很慢。
类型 | 类比 | 优点 | 缺点 |
---|---|---|---|
直接映射 | 固定车位 | 硬件简单、成本低,查找速度快 | 不灵活、易冲突、利用率低 |
全相联 | 随机车位 | 冲突小、利用率高 | 硬件复杂,查找速度慢 |
组相联 | 区域内随机车位 | 折中 | 折中 |
组相联是另外两种方式的折中:组之间是直接映射、组内是全相联。直接映射可以看作是组数 set = full 的特例,全相联可以看作是 set = 1 的特例。
替换策略
Q:如果一个 set 中没有可用的 way 时,应该把哪个 way 替换出去?
A:根据 associative
和实现复杂度三选一
LRU
(Least Recently Used)random
FIFO
根据理论分析,应该把最不活跃的数据替换出去,因为它再次被用的概率最小,即 LRU 策略。但是 LRU 的实现代价比较高,一般超过 8 way 就不可接受了,所以常用方法是 pseudo-LRU,用较小的代价实现近似 LRU 的效果。另外两种则很直观。根据《量化分析》的统计结果,在小容量时 LRU 的效果最好,当容量变大后,LRU 和 random 的效果差不多,FIFO 的效果则取决于具体程序。
写回策略
Q:cache 应该如何处理写回数据?
A: 实际需求和数据访问模式,还有其他约束条件共同决定
常见的组合方式:
hit | miss | |
---|---|---|
方式一 | write back | write allocate |
方式二 | write through | write non-allocate |
hit 下两种不同处理方式的对比:
策略 | 优点 | 缺点 | 应用 |
---|---|---|---|
write through |
硬件简单、flush 代价小 | 性能差 | L1 |
write back |
hit 效率高、带宽利用率高 | 硬件复杂、flush 代价高 | L2 之后 |
下面分类讨论不同策略的含义和优缺点。
Write-Through(WT) + write allocate
定义: hit 时同时写入 cache memory 和 lower memory;miss 时 allocate,先读回后写入
优点: 兼顾了 fast retrieval 和 data lost risk,维护一致性简单
缺点: 只加速 read;write latency 差
适用场景: write once,retrieval frequently 的场景(偶尔个别 write latency 可忍受)
Write-Back(WB) + write allocate
定义: hit 时写入 cache memory,不写入 lower memory;miss 时 allocate,先读回后写入
优点: 同时加速 read/write;兼顾了 fast retrieval 和 write latency
缺点: 有 data lost risk,维护一致性困难
适用场景: read-write 混合场景
Write-Around(WA)
定义: write through + write non-allocate
优点: 避免 pollution(一次性数据不会 flood),无 data lost risk,维护一致性简单
缺点: 只加速 read;write latency 差
适用场景: 不频繁 retrieval write data;stream 应用
Write-Invalid(WI)
定义: 只写入 lower memory,write hit 时 invalid 命中行
优点: 确保 read 性能,无 data lost risk,维护一致性简单
缺点: 只加速 read;write latency 差
适用场景: read intensive 应用
Write-Only(WO)
定义: write 同 write back,read 不会存储到 cache memory
优点: 确保 write 性能
缺点: 只加速 write;有 data lost risk,维护一致性困难
适用场景: write intensive 应用
Pass-Through(PT)
定义: bypass 所有请求到 lower memory
优点: N/A
缺点: N/A
适用场景: debug
Summary
policy | speedup | retrieval | write latency | consistency | data lost risk | speical |
---|---|---|---|---|---|---|
WT + WA | RO | + | + | + | ||
WB + WA | RW | + | + | RW mix | ||
WA | RO | +(RO) | + | + | Stream | |
WI | RO | +(RO) | + | + | Read intensive | |
WO | WO | +(WO) | + | Write intensive | ||
PT | debug |
实例:Arm M 系列 cache 规格
总结可以得到下面规律:
- Cortex-M 系列定位为 MCU,主要应用于嵌入式领域
- 中低端 core 内部没有集成任何类型的 cache(M0, M0+, M3, M4)
- 在系统层次,可以为 core 配置系统级别的 cache/TCM
- 高端 core 内部可能同时集成了 cache 和 TCM(M7)
- STM32F7 中集成的 M7 是一款双发射、6-stage 的超标量嵌入式处理器,core 内部同时配置了 cache 和 TCM
- I-cache 容量为 0~64KB,cache block 大小为 32B,2-way
- D-cache 容量为 0~64KB,cache block 大小为 32B,4-way
实例:Arm A 系列 cache 规格
总结可以得到下面规律:
- Cortex-A 系列定位移动端和用户级应用
- A 系列要支持 OS,所以全系配置了不同大小的 cache,舍弃了 TCM
- 低性能 core 只集成了 L1 cache,高性能 core 还集成了 L2 cache,甚至是 L3
实例:T-head C906
TODO
Cache 的性能指标
Q:如何评价一个 cache 的性能?
AMAT
Cache 最常用的性能指标是: AMAT
(Average memory aceess time) ,显然 Cache 系统设计越合理,对 core 表现出来的性能越好,AMAT 就越小。根据定义可以知道 AMAT 的计算公式如下:
\(AMAT = Time\ for\ a\ hit + Miss\ rate * Miss\ penalty\)
对于多级 Cache 系统,AMAT 公式如下(以两级 Cache 为例):
\(T_{avg}=H_1*C_1 + (1-H_1)*(H_2*(C_1 + C_2) + (1-H_2)*(C_1 + C_2 + M)\)
每个符号的含义:
- \(H_1\) 表示 L1 cache 的命中率
- \(H_2\) 表示 L2 cache 的命中率
- \(C_1\) 表示 L1 cache 命中访问时间
- \(C_2\) 表示 L2 cache 命中访问时间(即 L1 miss 但是 L2 hit 的 penalty)
- \(M\) 表示 DDR 的访问时间(即 L2 miss 的 penalty)
也可以换一种算法:
\(T_{avg}= C_1 + (1-H_1)*C_2 + (1-H_1)*(1-H_2)*M\)
可以证明两种方式是等价的。
其他指标
AMAT 是 dcache 对外表现出来的综合性指标,如果想评估深入评估 dcache 微架构,那么应该用更多的细分指标来衡量。
最基本的指标是所有硬件都适用的 throughput
和 latency
,描述 cache 响应命令的吞吐率和时延。
Cache 特有的最简单的指标是命中率 hit ratio
,描述 cache 规格 / 微架构是否合理,合理的设计命中率都比较高,95% 以上。
另外如果想衡量 prefetch 的性能,主要有两个指标 coverage
和 accuracy
,
coverage 描述了漏警性能,accuracy 描述了虚警性能,一般这两者很难兼得。同理,victim cache 也可以用类似的指标来衡量。
Cache 的性能分析模型
3C 模型,也就是分析清楚 cache 的 miss 可以分为几类,每一类的产生原因是什么:
compulsory miss
capacity miss
conflict miss
一旦知道了 miss 产生的原因,也就可以针对性地采用各种方法来降低。虽然按照 3C 模型来分析时,优化某个因素的行为可能会导致另外一个因素的恶化,但是总体上它仍然是个很有用的工具,可以帮助我们在做设计时对 cache 性能进行建模(而且目前我们也没有更好的模型)。
Note
当系统中有多个 cache 时,会额外再增加一个 C coherency miss
,即为了保持一致性导致 cache 进行 flush,产生 miss。此时就是 4C 模型。
Cache 性能优化方法
有了 cache 模型,就可以根据模型来优化性能,针对性能公式中的每个因子,优化思路可以分为下面几类:
优化思路 | 优化方法 |
---|---|
减小 hit time | 小而简单的 L1 cache、路预测、Vitural Index/Physical Tag、serial 访问 |
减小 miss rate | 增大容量、增大 block size、增加关联度、预取、软件优化 |
减小 miss penalty | victim cache、write buffer、多级 cache、关键字优先、写合并 |
提高 throughput | pipeline、multibank( 多端口 )、非阻塞 |
Note
miss penality 本质是就是一块数据的搬运耗时,可以借用 “ 火车过山洞 ” 模型:
假设火车长度为 l,火车的时速为 v,火车头进入山洞的时刻为 t1,火车头出山洞的时间为 t2,火车尾出山洞的时刻为 t3,那么可以将火车过山洞的过程和数据搬运过程对应起来
火车过山洞 | 搬运数据 |
---|---|
t2-t1 | latency |
v | throughput |
l | data-amount |
t3-t2 = l/v | transfer-time = data-amount/throughput |
火车过山洞的总时间 = t3 - t1 = (t3-t2) + (t2-t1)
数据传输的总时间 = transfer-time + latency
所以优化 miss penality 的方法主要就是下面这 3 种:
- 减小 latency,这个取决于 hierarchy 中的下一级, cache 很难改变
- 减小 data-amout,每次 miss 时少取一些数据
- 增加 throughput,提高 cache 和 hierarchy 下一级之间的传输带宽
Small and Simple L1 Cache
面临的问题:复杂 L1 的速度很难跟上 core 的时钟频率。
解决思路:思路 1(减小 hit time),简化硬件设计、减小 size 和 associativity,从而减小 hit time。
付出的代价:miss rate 增加,需要下级 cache 作为补充。
Way Prediction
面临的问题:对于并行访问 tag 和 data 的组相联 cache,必须经过下面 3 个步骤,
- 读 tag memory
- 比较 tag 内容
- 根据读 tag 的结果选中 & 操作 data memory(mux + 写 data memory)
整个过程组合逻辑很长,hit time 较长,所以时钟频率无法做到很高。
解决思路:思路 1(减小 hit time),采用 prediction bits,把组相联的 cache 当初直接相联来用,那么第二步的比较 tag 就只有 1 个 way 做比较,如果命中则相当于直接相联 cache 结构;否则在下个周期检查剩余 way 的 tag。因为这种方式第二步的组合逻辑变少,hit time 就变短了。
付出的代价:使得 pipeline 难以实现。
Note
还有一种更进一步的做法叫做 way selection
,即第一步也只读 1 个 way 的 tag,如果 miss 则需要重新读剩余 tag、作比较、操作 data memory。这种方法显然可以更省功耗,但是缺点就是一旦 miss,付出的代价很大,因为要完成重新执行一遍步骤 123。
Vitural Index/Physical Tag
面临的问题:全部使用虚拟地址时,必须经过一道查询 TLB 的过程,增加了整体的访问 latency。
解决思路:思路 1(减小 hit time),采用 page offset 作为 cache tag,省去查询 TLB 的过程。
付出的代价:实现复杂度增加,功耗增加。
Serial 访问
- parrel:tag array 和 data array 并行访问,根据 tag array 的读结果产生 mux 信号,从 data array 的结果中选出目标 way
- serial:先访问 tag array,根据结果产生 data array 的片选信号,直接读出目标 way
面临的问题:parrel 方式 critial path 较长,频率低
解决思路:思路 1(减小 hit time),采用 serial 方式优化 critical path,同时因为省去了不必要的读 data array,所以功耗也更低。
Cache Size
面临的问题:cache 容量不够,数据频繁被替换。
解决思路:思路 2(降低 miss rate),降低 capacity miss
付出的代价:导致 hit time 变大,同时成本和功耗也会变高。
Block Size
面临的问题:block 太小,对空间局部性的利用不充分。
解决思路:思路 2(降低 miss rate),可以充分利用空间局部性,降低 compulsory miss。而且扩大 block size 会导致 tag 位宽变小,相应地可以减小一点功耗。
付出的代价:导致 miss penality 变大;而且过大的 blocksize 会适得其反。
Set Associativity
面临的问题:关联度太小,一个 set 内频繁竞争。
解决思路:思路 2(降低 miss rate),降低 conflict miss。
付出的代价:导致 hit time 变大,同时成本的功耗也会变高。
Prefetch
面临的问题:如果每次发生 miss 时只取回当前 cache line,那么 cache 向 DDR 发送的 burst len 和 outstanding 都很小,效率很低。频繁发生 compulsory miss。
解决思路:思路 2(降低 miss rate)。在取回当前 cache line 的同时以大 burst len 和 outstanding 高效地多取一些相邻数据,这样访问这些预取数据时就不会发生 miss。
- 软件预取:有些 ISA 定义了预取指令,程序员可以通过软件进行预取
- register prefetch:把数据预取到 register 中
- cache prefetch:把数据预取到 cache 中
- 硬件预取:cache 自主可以观测 unit-stride 和 stride 的规律,自动预取数据
方案 | 含义 |
---|---|
OBL (one block look-ahead) |
每次多预取一个 cache block |
stream buffer |
多预取的数据存储在 stream buffer 中,miss 时再写入 cache line |
SPT (stride predict table) |
硬件检测 load 是否存在 stride 模式,取回数据直接写入 cache line |
stream cache |
结合 steam buffer 和 SPT,把预取回来的数据放在一个小 cache 中 |
预取数据不直接存到 cache 中的原因是避免 “cache 污染 ”,但是 stream buffer 不灵活,所以改进方案是把预取数据放到一个 stream cache 中。
付出的代价:硬件复杂度增加,消耗更多资源。
Note
prefetch 有效的前提是有剩余带宽未被利用。如果 prefetch 干扰了正常 miss 的读取,那么反而会降低性能。
Software Optimize
纯软件,不需要改任何硬件。
同样也可以分为两类:改善 miss rate 或者是改善 miss penality。
- loop interchange:交换嵌套 loop 的顺序
- bloking:对数据分块处理
Vicitm Cache
面临的问题:conflict miss 导致频繁的读写下一级 memory,导致整体性能降低。增加相联度代价太大,其他 set 没有这个需求。
解决思路:思路 2(降低 miss rate),另外增加一个小容量(通常 4~16 个数据)、全相联的 cache,缓存被替换出来的数据。一般和 main cache 为 exclusive 关系。
和 Victim Cache 相对应的还有一种 Filter Cache,即在数据进入 main cache 前,先写入 Filter cache,等数据再次被使用时才写入 main cache,用来过滤偶然数据,提高整体利用率。
付出的代价:硬件复杂度增加。维护 victim cache 和 main cache 之间的一致性。
Write Buffer( 读优先 )
面临的问题:如果发生 miss 时被替换的 block 为 dirty,则必须先将其写回下级 memory 后才能把目标 block 读进来,整个过程是串行的。当写下级的代价很高时,会导致 miss penality 很大。
解决思路:思路 3(减小 miss penality),先将 dirty block 写入一本本地的 write buffer,为目标 block 尽早腾出空间。等下级 memory 空闲时,再将 dirty block 写入其中。
- 对于 write-back 的 cache,就是把 dirty cache line 整条都写入 write buffer
- 对于 write-through 的 cahe,就是把 dirty data 写入 write buffer
L1 D-cache 通常采用 write-through 方案,配合 write buffer 提高性能。
付出的代价:硬件复杂度增加。cache 发生 miss 时首先要查询 write buffer(需要 CAM)
Write Merging
面临的问题:如果 core 每次只写一个 word,那么普通 write buffer 的每个 entry 的大部分空间都会被浪费掉,write buffer 很容易达到 full。
解决思路:思路 3(减小 miss penality),每次把数据写入 write buffer 时,检查是否可以合并到已有 entry 中。
付出的代价:硬件复杂度增加。
Multiple Level
面临的问题:单级 cache 无法同时满足 fast hit 和 few miss 的需求。
解决思路:思路 3(减小 miss penality),存储器层次结构 L1 + L2 + L3。一般 L1/L2 为每个 core 私有,L2/L3 共享。
- L1: 小容量、低关联度、write-through
- L2/L3: 大容量、高关联度、write-back
付出的代价:面积变大、解决一致性问题、硬件复杂度增加。
Critical Word First
面临的问题:core 每次访问实际上只需要一个 word,但是 cache miss 时取要取回整个 block,耗时较长。
解决思路:思路 3(减小 miss penality),采用 “ 不耐心 ” 的做法,优先向下级 memory 请求 miss 的 word,一旦读回来立即返回给 core。
付出的代价:硬件控制复杂化。
Early Restart
面临的问题:同 Critical word first
解决思路:思路 3(减小 miss penality),另外一种 “ 不耐心 ” 的做法,按照正常顺序请求数据,但是取回数据后立即把 word 发给 core。
付出的代价:硬件控制复杂化。
Note
critical word first 和 early restart 只有在 block size 很大时收益才比较明显。
Pipeline
面临的问题:读 D-cache 时 tag memory 和 data memory 可以并行同时读;但是对于写 D-cache,必须经过 way prediction 小节中提到的 3 个过程。整个过程串行操作时钟频率会很低,一般的做法是分为两个 cycle,第一拍读 tag 作比较,第二拍写 data memory。此时 throughput 为 0.5 instr/cycle。
解决思路:思路 4(提高 throughput),将整个过程 pipeline 化,达到 1 instr/cycle 的 throughput。
付出的代价:硬件复杂度增加。后续指令要额外检查 pipeline 上的数据,增加 forward 通路。显然 pipeline 越深,mispredict 时 flush 的代价就越大,同时 load-to-use 的 latency 也越大。
Note
pipeline 主要应用在 L1 上,因为它的访问带宽会限制 instruction throughput。目前大多数 core 的 L1 都采用 3~4 级 pipeline 的方式。
Multibank
面临的问题:单 bank 的最高 throughput = 1 instr/cycle,无法满足超标量处理器的需求
解决思路:思路 4(提高 throughput)。多 bank(多端口)有几种常见方案:
一般多端口的实现方案有以下几张:
实现方案 | 含义 | 备注 |
---|---|---|
true multiple port |
真正的多端口,memory 有相应数量的读写端口 | 实现代价太大,不可接受 |
virtual multiple port |
memory 仍然是单端口,cache 频率是 core 的倍数 | 可扩展性差,现在不可接受 |
copy multiple port |
memory 仍然是单端口,但是复制多份,保持 copy 之间的同步 | 浪费资源,同步控制复杂 |
multiple bank |
memory 仍然是单端口,按照地址分 bank 交织 | 折中方案,普遍应用 |
分 bank 按照地址交织,也有一些缺点,比如要依靠编译器降低冲突概率;发生冲突时性能变差;内部 crossbar 对 PR 不友好,但是相比于其他几个方案,代价最小,应用最广泛。
付出的代价:增加硬件复杂度,消耗更多资源。
Note
multibank 应用在 L1 时主要为了提供高 throughput,应用于 L2, L3 时更主要的目标是做功耗控制。
Non-blocking
面临的问题:miss 会阻塞 core 的流水线,效率低。
解决思路:思路 4(提高 throughput),cache 在处理当前 miss 的同时处理后续的请求。
付出的代价:增加硬件复杂度,消耗更多资源(MSHR)。
in-cache MSHR
:给 tag array 新增额外的 1bit transient 信号,如果某 entry 处于 transient mode,tag array 保存 base address,data array 保存所有的 MSHR 信息,具体形式既可以是 implicit 也可以是 explicit。
MSHR 实现方式 | 优点 | 缺点 |
---|---|---|
implicitly | 消耗资源少(不需要记录 offset) | 一个 word 只能 miss 一次、深度固定 |
explicitly | 同一个 word 可以 miss 多次、深度灵活可配 | 消耗资源多(需要额外记录 offset) |
in-cache | 不需要任何额外资源保存 MSHR | 处理步骤增多,latency 变大 |
参考资料
Computer Organization and Design RISC-V Edition. David A. Patterson, John L. Hennessy
Computer Architecture: A Quantitative Approach. John L. Hennessy, David A. Patterson
Processor Microarchitecture: An Implementation Perspective. Antonio Gonzalez
《计算机体系结构》 胡伟武
《超标量处理器》 姚永斌