共计 2731 个字符,预计需要花费 7 分钟才能阅读完成。
某个下午偶然间看到了 MESI 缓存一致性协定,引出了我不少相干的纳闷,写下此文记录
通过这篇文章你能理解到的常识
- MESI 协定是什么,解决了什么问题
- 指令重排 是什么,解决了什么问题
- 内存屏障 是什么,解决了什么
- MESI 与 并发同步的区别
MESI 缓存一致性协定
MESI 产生的前提
- 多级缓存的呈现
- 多核 CPU 的呈现
总之为了计算机的性能,古代计算机都具备上述两点的设计,下图为不同存储的 IO 速度比照
MESI 解决的问题
因为当初计算机都是多核 CPU 了,并且每一个 CPU 都有本人独立的缓存(如下图架构),这样就会有可能多个 cpu 操作同一份数据,导致各个 cpu 缓存中的同一份数据值不统一的状况。
MESI 的具体设计
MESI:由缓存行(缓存中操作的根本单位,相似于磁盘的页)的四个状态首字母组成:
- Modified(批改):这一行数据批改过了,与内存中的数据不统一了,这意味着如果其它 cpu 中的缓存具备这行数据,其状态要批改成 invalid
- Exclusive(独占):只有一个 cache 读了这个数据行,并且没有批改过
- Shared(共享):有多个 cpu 的 cahe 读了这个数据行,没有被批改过
- Invalid(有效):该缓存这一行的数据有效
状态流转
缓存行的状态流转如下图,通过对各个缓存行状态位的管制,达到了多核 cpu 缓存中数据统一的目标
一个例子
- 当初有两个 cpu,假设其缓存都为空,内存中有一个 x = 0 的数据
- cpu1 通过 bus 从内存中读取 x,该缓存行状态设置为 E
- cpu2 通过 bus 从内存中读取 x,cpu 1 嗅探到地址抵触,两个 cpu 中 x 所在缓存行状态位设置为 S
- cpu1 须要批改其缓存中 x 的值,设置其 x 所在缓存行状态为 M,告诉 cpu2 把 x 所在缓存行状态位设置为 I,再批改缓存中 x 的值
模仿网站:https://www.scss.tcd.ie/Jerem…(略微有一些差别)
带来的问题
多个 cpu 的缓存状态置换是须要耗费工夫,当一个 cpu 中缓存切换状态时,这个 CPU 须要期待其余 CPU 收到音讯实现各自缓存中相应数据的状态切换并且收回回应音讯。可能呈现的阻塞都会导致各种各样的性能问题和稳定性问题。
实际上,cpu 齐全能够利用缓存行状态切换期待的这段时间去执行下一个指令,这就引出下文的指令重排
指令重排与内存屏障
上文中提到 cpu 能够不期待以后指令后果,间接去执行下一条指令,这其实就被称之为指令重排。
当然指令重排分为两个期间:编译期间,运行期。指令重排的目起都是为了谋求性能,在以不扭转原语义后果前提下乱序执行。这里的指令重排显然属于运行期,这里不探讨编译器产生的指令重排。
指令重排的实现
存储缓存(store buffer):
- 之前须要同步期待其它 cpu 返回的音讯确认,而后批改缓存中的值
- 当初间接把以后指令批改的后果放在存储缓存中,而后间接去执行下一次指令,等到异步收到其它 cpu 的确认音讯后,再把存储缓存中要批改的值同步到缓存中
生效队列(invalidate queue):
- 之前当 cpu 检测到其它 cpu 收回的生效告诉时,须要以后 cpu 进行手上的工作,实现对应缓存行的状态切换,回复确认音讯
- 当初间接把生效告诉放在生效队列中,立马返回确认音讯,当前再缓缓解决生效队列里的音讯
指令重排带来的问题
并行环境下可见性问题
上述指令重排带来了并行环境下的可见性问题,因为上述的实现导致了处理器对数据的批改不是立刻对其余内核可见的(store buffer 与 invalidate queue 都是异步解决的),这样在并发运行的程序下有可能会有数据不统一的产生。
内存屏障
cpu 并不知道什么指令可能重排序,什么指令不可能重排序,但 cpu 把这个工作交给了软件(程序员),这就是内存屏障。
内存屏障又分为四种:LoadLoad Barriers(读屏障),StoreStore Barriers(写屏障),LoadStore Barriers,StoreLoad Barriers
不同处理器对内存屏障的实现是不一样的,这里咱们来剖析下 x86 架构
读屏障
作用:所有读屏障之前产生的内存更新,对读屏障之后的 load 操作都是可见的
cpu 实际操作: 把 生效队列(invalidate queue)里的实效指令(I)全副执行
写屏障
作用:所有写屏障之前产生的内存更新(M)对之后的命令都是可见的
cpu 实际操作:等到 存储缓存(store buffer)为空(所有更新已刷出),cpu 能力执行写屏障之后指令
Full 屏障
作用:上述二者之和
cpu 实际操作:上述二者之后
总结
多核多级缓存计算机:进步单核无缓存计算机的性能,但会有缓存一致性问题
MESI:解决多核计算机缓存一致性问题,引发性能慢的问题
指令重排:缓解 MESI 性能问题,引出可见性问题(其实还是缓存不统一,无奈保障实时性)
内存屏障:把并发存在的可见性问题交给软件(程序员)去解决,软件来通知 cpu 哪些指令不能重排序
总之这一系列下来是为了进步了计算机性能和保障了缓存数据一致性
思考
MESI 缓存一致性 与 并发同步的区别?
一个是作用于 CPU(缓存),一个作用于线程。一个 cpu 能够对应多个线程。实际上并发同步思考的是线程与内存交互,并看不到两头还有缓存。
咱们先构想在单核机器上
- 单核机器不存在缓存一致性问题,但还会存在多线程并发同步的问题
如果在多核机器上
- 假如有 a,b 两个线程别离运行在 cpu1,cpu2 上,内存中有个变量 x,两个线程它们操作是对 x++。
- 并发管制须要对 x++ 这一操作加锁,意思就是两个线程不能同时对 x 进行操作,两个线程运行 x++ 工夫必须是串行的,即便它们别离在两个 cpu 上
- 在并发管制中看到的是:a 线程对 x++,把 x=1 写入内存,而后 b 线程读取内存中 x 的值,x++,把 x=2 写入内存
- 然而在 cpu 看来,它是间接与缓存打交道的。如果没有 MESI 缓存一致性协定,它看到有可能的是:a 线程从 cpu1 缓存中读到 x=0,x++,把 x=1 写入缓存,而后 b 线程从 cpu2 的缓存中读到 x = 0,把 x = 1 写入缓存。
- 但实际上,并发管制是依赖 MESI 缓存一致性协定的。a 线程从 cpu1 的缓存中读到 x=0,x++,把 x=1 写入缓存,同时使 cpu2 上的 x=0 的缓存生效并且写回到内存。而后因为线程 2 中 x 的缓存是生效的,只能从内存中读取 x=1,x ++,把 x=2 写入内存 同时使 cpu1 上 x=1 的缓存生效。
总结来说并发管制保障多线程在临界区上串行执行,而在多核计算机中,又依赖缓存一致性保障后果的正确性。
参考资料
- https://zhuanlan.zhihu.com/p/…
- https://www.cnblogs.com/hello…
- https://stackoverflow.com/que…
- https://www.zhihu.com/questio…