Abstract
single cache line问题
现代CPU一般都会有三级缓存L1, L2, L3。L1缓存的每个CPU独立的,L2L3是CPU间共享的。cache的存在读时可以提高读取速度,写时可以防止直接写到内存(write back, write through)
1 | ┌──────┐ ┌──────┐ ┌──────┐ |
这样一来,CPU间的L1缓存就有可能保存有各自不同的数据,从而导致一致性问题。那么就就有了缓存一致性协议
Q: 为什么L1缓存是CPU间独立的?
- 因为L1已经很小了,如果时CPU间共享的很容易就因为单点竞争成为瓶颈节点
- 如果是像L2在CPU外,CPU间共享的,那么就会导致硬件不好分布,里核心远(NUCA(非一致缓存访问)问题)
MSI
MSI分别表示”Modified”, “Shared”, “Invalid”,用于标识当前缓存的状态,cpu间需要通过内部总线(缓存总线)相互通信来达成共识。
- 当状态为”I”时表示缓存失效,需要(通过L2)去其他状态为”S”或”M”的CPU处拿去数据
- 当状态为”M”时表示缓存被当前CPU修改了,是独占的
- 当状态为”S”时表示当前CPU已经从另一个”M”的CPU获取到了数据,那么这两个U都变为了”S”态
CPU需要一种类数据结构: 目录项(directory协议)来实现缓存一致性。这个目录项会记录全局的一个bit vector,用于指示哪个CPU是持有数据的(S或M)。所以一共是两个结构:
- 全局目录项
- CPU状态记录
1 | CPU 0 状态记录的大致结构 |
为了方便期间,下面的说明就把CPU的状态直接写道bit vector里了
1 | | addr | dirty | CPU1 | CPU2 | CPU3 | |
假设刚开始3个CPU都是”S”态,共享缓存。这时CPU1修改了数据,则缓存也发生了修改,那么通过短暂的”沟通”(cycle)其他CPU缓存的状态就变为了”I”
1 | | CPU1 | CPU2 | CPU3 | |
这时CPU2再对相同的数据进行了修改,那么CPU2变为M,其他变为I。
1 | | CPU1 | CPU2 | CPU3 | |
值得注意的是,虽然CPU1之前修改了数据,但是CPU2的修改并不需要”取回再该”,因为同一份数据修改是覆盖的嘛。这就是缓存带来的一个优点。
这时,CPU3需要读取数据,通过缓存发现该数据的cache的”I”的,那么它就会去其他”M”或”S”态的CPU获取缓存。之后变为
1 | | CPU1 | CPU2 | CPU3 | |
缓存一致性带来的问题
可以看到,引入缓存一致性导致了通信成本的提高,如果CPU数量再多点,那么”查表”将不是一件非常简单是事。本来一个cycle可以执行完成的操作需要”遍历所有”才能继续。
所以会发现随着CPU(core)的增加,并发程序的性能反而下降的现象。这就是no-scalable问题。
导致这个问题的原因就是single cache line