本文没有实例代码,都是教科书似的知识,但是知识特别重要! 所以要用心看,我会尽我最大输出能力来写。
CPU 高速缓存
计算机在执行程序的时候,每条指令都是在 CPU 中执行的,而执行的时候,又免不了要和数据打交道。数据是存放在主存当中的,也就是RAM (计算机的物理内存)。
但是物理内存速度远远是跟不是CPU执行速度的,于是就在CPU层加上了「CPU 高速缓存」
CPU多级缓存
由于CPU的运算速度远远超越了1级缓存的数据I\O能力,CPU厂商又引入了多级的缓存结构。通常L1、L2 是每个CPU 核有一个,L3 是多个核共用一个。
Cache Line
CPU多级缓存 是由很多个「缓存行」(Cache line) 组成的。Cache line 是 CPU多级缓存和 RAM 交换数据的最小单位。
带有高速缓存的CPU执行计算的流程
- 程序以及数据被加载到主内存
- 指令和数据被加载到CPU的高速缓存
- CPU执行指令,把结果写到高速缓存
- 高速缓存中的数据写回主内存
缓存一致性
既然每个核中都有单独的缓存,那我的 4 核 8 线程 CPU 处理主内存数据的时候,不就会出现数据不一致问题了吗?
为了解决这个问题,先后有过两种方法:总线锁机制和缓存锁机制。
总线锁就是使用 CPU 提供的一个LOCK#信号,当一个处理器在总线上输出此信号,其他处理器的请求将被阻塞,那么该处理器就可以独占共享锁。这样就保证了数据一致性。
但是总线锁开销太大,我们需要控制锁的粒度,所以又有了缓存锁,核心就是“缓存一致性协议”,不同的 CPU 硬件厂商实现方式稍有不同,有MSI、MESI、MOSI等。
缓存锁机制 MESI 协议
MSEI协议是多核cpu保证cache一致性的一种方法,他们有下面几种状态:
M(Modified) : 缓存行是脏的(dirty),与主存RAM的值不同。如果别的CPU内核要读主存这块数据,该缓存行必须回写到主存,状态变为共享(S)。
E(Exclusive) : 缓存行只在当前缓存中,但是干净的(clean) 缓存数据与主存RAM数据一致。当别的缓存读取它时,状态变为共享(S);当前写数据时,变为修改(M)。
S(Shared) : 缓存行也存在于其它缓存中且是干净的。缓存行可以在任意时刻抛弃。
I(Invalid ) : 缓存行是无效的。
M-修改 E-独占 S-共享 I-无效
假设有二核CPU A、B对应三个缓存分别是cache 1、2。在主内存中定义了a的值为0。
1. CPU 读取数据的流程
- CPU A发出读取指令。
- CPU A从RAM中通过bus总线读取a到cache1中,并设置该cache line的状态为E-独占。
- CPU B 试图从RAM中读取a时,CPU A 检测到了地址冲突。这时CPU A对相关数据做出响应。此时a 存储于cache 1和cache 2中,a在chche 1和cache 2中都被设置为S-共享。 【E-独占 ===> S-共享】
2. CPU A 修改数据的流程
- CPU A 计算完成发出指令要修改a。
- CPU A 将a设置为 M-修改 并通知缓存了a的CPU B, CPU B将本地cache 2中的a设置为 I-无效,CPU B 要读的话判断是I-无效是就直接拿主存。
- CPU A 对a进行赋值。
3. CPU B 读取数据的流程
- CPU B 发出了要读取a的指令。
- CPU B 判断是当前是无效状态, 就通知 CPU A,CPU A将修改后的数据同步到主内存时 cache 1 修改为 E-独占 , CPU B直接读取主内存并设置为 S-共享。
每个CPU读取完缓存行之后都在内存中监听已读缓存行的状态,这时CPU b就会监听的缓存行a已被修改,此时,CPU b就会把他设置为Invalid(无效)状态,无效状态的数据会被丢弃,如果想继续操作的话,还需要到主存中重新获取。
参考资料
https://xie.infoq.cn/article/5f4397e93c26c89c9f7b144bf
《 圣人自知不自见;自爱不自贵 》
释义:有自知之明,而且也不自我表现;有自爱之心也不自显高贵。