缓存一致性

通信模式

共享存储

统一的地址空间,每一个处理器都可以访问。但是需要注意并发控制。

使用线程。

消息传递

使用单独的地址

 

共享存储系统

因为性能原因,使用多个私有缓存

当一个chip读取一个值时,必须读取到最近写入的值。

缓存一致性(Cache Coherence):执行一个读操作,应该返回哪一个值。

存储一致性(Memory Consistency):如何让一个写操作让所有人都看到。

 

缓存一致性(Cache Coherence)

cache1中是2,但是之后cache3中写入了值3。

处理器核3若读取到2,那么读到了一个旧值,应当读到most recent值3.

一致性协议

写传播:保证一个写最终让所有处理器看到。

写序列:一堆写操作应当能够序列化。

如何保证写传播

写无效协议:在执行一个写操作时,将其他所有的副本无效。

写更新协议:在执行写操作后,更新所有副本

如何观测共享状态并序列化请求

基于侦听:所有cache通过共享总线观察其他cache的动作。

基于目录:多处理器维持一个目录,目录会跟踪所有私有缓存内容,然后进行排序。

基于侦听

由总线确保序列化。

        1. 广播模式,确保所有有序。
        2. 每一个缓存控制器侦听所有总线事务。
        3. 控制器更新缓存的状态。
        4. 使用写传播策略。

基于侦听序列化请求——VI协议(Valid/Invalid)

VI协议四种动作

处理器读:处理器自己的缓存中load一个值。

处理器写:处理器会写入一个值到cache。(会触发总线写,写入主存,同时让其他cache都无效)

总线读:触发总线读的处理器,从主存load一个值到自己的cache

总线写:发出总线写的处理器,将自己的cache写入到主存。其他处理器收到总线写消息,将自己的cache设置为Invalid

VI协议两种状态

Valid:缓存数据有效,可以直接读。

Invalid:缓存数据无效,只能从主存读,触发总线读,加载数据到cache。

 

VI协议的缺陷

每一次写都要更新主存。(因为处理器写会触发总线写)

每次写都需要广播。

状态变换图

Valid -> Valid :  处理器读(读一个valid的cache),处理器写,总线写。

Valid -> Invalid:响应总线上的总线写。

Invalid -> Valid: 处理器读,总线读

Invalid-> Invalid:处理器写,总线写

实线是响应处理器的动作,虚线是响应总线的动作。

core0 load一个值0xA,但是缓存中没有,触发总线读,从主存load一个值到cache.

core0 store值3到缓存,触发总线写,,将cache更新到主存,同时使得其他core的cache变为Invalid。

core1 load缓存中的值时,发现缓存中的值为Invalid,于是触发总线读,将主存的值load到cache中

 

特点

读取

if cache.state=='Invalid' :

   总线读,从主存读,且从load一个值到cache。

else :

        直接读取缓存。

写入

总线写,将自己cache值写入到主存。

让其他core的cache变为无效

 

基于侦听序列化请求——MSI协议

  三种状态

Modified:当前Cache已修改,缓存中数据与主存数据不一致(会触发总线排他读,其他cache变为无效,有效且修改过)

Shared:表示当前的处理器已经从另一个被Modified的处理器中获取到了数据。(有效且是最新的)

Invalid:当前Cache块失效,需要从其他状态为“S”或“M”的CPU出拿数据。

每一个Cache修改了一个数据后,其他Cache会被标记为无效。

 

五种动作

处理器读:处理器自己的缓存中load一个值。

处理器写:处理器会写入一个值到cache。(会触发总线写,写入主存,同时让其他cache都无效)

总线读:触发总线读的处理器,从主存load一个值到自己的cache

总线写:发出总线写的处理器,将自己的cache写入到主存。其他处理器收到总线写消息,将自己的cache设置为Invalid

总写排他读:触发总线读的处理器,将其他的cache状态变为Invalid,自己状态变为Modify

     

缺点

收到其他core发出的总线排他读,自己cache变为Invalid,其他某个core的cache中是最新的,但是没写入到主存。

若这时候读取cache,会触发总线读,直接从主存中读取一个值,只存中的值是旧的。

状态变化

 Shared -> Modified :向自己的缓存中写入一个值,但是没写入主存,状态变为Modified。

    Shared -> Invalid:其它Core将最新的值写入其缓存,触发总线排他读,收到总线排他读表示自己是旧的无法使用,状态变为Invalid。

    Invalid -> Modified:向自己的Core写入一个值。

 

    Invalid -> Shared:读取的时候自己是Invalid,于是状态为Modified的cache会将值写会主存,然后自己总线读一个值,这个值就是最新的。

    Modified -> Shared:其他cache为Invalid时被读取,自己将cache写入主存以便其他core读取,于是自己的变为最新的,状态为Shared。

后两条的前提读取Invalid时触发的cache miss 能够被识别,否则会导致读取旧的数据。

案例

Core0 load一个值,发现cache中没有数据,于是触发总线读,从主存读取一个值到自己的cache。

Core1 load一个值,发现cache中没有数据,于是触发总线读,从主存读取一个值到自己的cache。

Core0 store一个值10到自己的cache,自己的状态变为Modify,触发总线排他读,其他Core的Cache变为无效。(表示自己是最新的)

core1 store一个值10到自己的cache,自己状态变为Modify,触发总线排他读。其他core的状态变为Invalid。(表示自己的最新的)

core0 load一个值,发现自己的cache状态为Invalid,那么Core1会使用总线写将自己的cache写入主存,然后Core0从主存load一个值到自己cache。(主存中值可能是旧的值,这也是缺点)

特点

写入cache时,仅仅是变为Modify,不写入主存,然后发出总线排他读,并使得其它CPU的cache无效。

但是cache状态为Invalid时若读取,则会读取主存的数据,但是主存中数据可能是旧的。

解决方法

增加一个Exclusive状态:如果没有其他的share,也就是没有最新的未写入到主存的值,一个读取应该变成E状态,且不触发总线读。(用E表示从主存读到最新的)

增加Owner状态:负责写回,相当于S状态+负责写回。

当从M状态到S状态时,其他处于M状态的变成O状态而不是S

切分事务

打破了总线事务的原子性。

可以支持同时多事务执行。

优点 

拥有更高的吞吐率。

缺点

但是响应不一定是顺序

基于目录

特点

通过目录路由所有一致事务

非广播,乱序网络

缓存一致性——假共享

产生的原因:缓存一致性是块级别而不是word级别,但是一个块可以有多个word。

P1写wordi,P2写wordk,两个word由相同的块地址。

可能导致冲突,P1写完使得P2写的无效,P2写了后导致P1写的值无效。(Ping-Pong效果。)

 

缓存一致性——总线占用

一个原子操作read-modify-write需要两个内存操作。而不用其他处理器介入内存操作。

在多处理器环境下,总线在原子读或写操作整个期间被锁住

LR/SC指令

产生原因

总线事务的总数没必要减少,可以将原子指令切分为LR和SC指令。

Load Reserve

指令格式:lr.{w/d}.{aqrl} rd,(rs1)

作用:读取保留,将rs1中内容加载到rd寄存器,在rs1对应地址上设置保留标记

Store Conditional

指令格式:sc.{w/d}.{aqrl} rd,rs2,(rs1)。

作用:条件存储,根据条件将值将rs2写入到rs1内,不满足条件则不读。

切分为LR和SC指令的好处:

增加总线利用率。

避免了缓存ping-pong效应。(因为进程只要尝试获取互斥锁,得到了互斥锁才写,而不用每次尝试执行store操作)

 

 

 

 

 

 

 

posted @   Laplace蒜子  阅读(92)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示