Memory Consistency与Cache Coherence之间究竟有何神秘联系?

摘要:A Primer Of CC and MC - 对于 MC 和 CC 的一点思考 前言 这个专栏是一个全新的专栏,旨在记录我学习书本 A Primer Of CC And MC 的学习过程。 最近在自制 OS 内核,自然而然的搞到了并发,结
A Primer Of CC and MC - 对于 MC 和 CC 的一点思考 前言 这个专栏是一个全新的专栏,旨在记录我学习书本 A Primer Of CC And MC 的学习过程。 最近在自制 OS 内核,自然而然的搞到了并发,结果很快就被 -O2 优化教做人了。遂不服气,开始研究 CC (Cache Coherence) 和 MC(Memory Consistency), 势必要搞出点名头。 1 缓存和乱序执行 1.1 缓存的诞生 很久很久以前,CPU想获取或者写入数据,都是直接控制总线读写内存。诶,这多方便,多直接,不是吗?但是,很快架构师就发现,这 DRAM 的速度相对于cpu的寄存器而言,可实在是慢得不敢恭维啊。那怎么办? 于是,我们不得不请出那句计算机界的至理名言(好吧,其实是冷笑话)了: 所有的问题都可以通过加一层抽象层来解决,如果加一层解决不掉,那就--再加一层! (记住这句话,后面讲 MC 和 CC 的关系的时候要考!) 所以,经研究决定,我们决定给 CPU 加一层抽象--缓存。对,加一块容量比寄存器稍微大一点,但是速度又比内存快得多的SRAM。我们将所有常用的数据集成在缓存中,需要的时候快速取就好。这不就解决了速度问题了吗? 但是,如果程序员在写汇编代码的时候还得管缓存的话,那可就麻烦死了。所以,我们必须保证,程序员在写汇编代码的时候,脑子中只有CPU和内存,而没有缓存,这样才不至于让程序员在写代码的时候由于脑子过载而死机。由此,我们可以引出缓存的一个特点,那就是透明性。 1.2 CPU 的乱序执行 还是在很久很久以前,那会 CPU 非常的呆,看到内存中的指令只会 FDEMW ,这多符合直觉呀。 但是,很快架构师就发现,诶,我只需要略施小计,对指令进行重排列,就可以加快执行速度呢! 所以,后续的 CPU 就引入了一个新技术 -- 乱序执行,它能够对指令进行重排列,在保证最终结果一致的前提下更换指令的顺序。 这个"最终结果一致",说人话,就是重新排列该核心中数条彼此无关的指令的执行顺序。例如 mov eax,1 ; Instruction A mov ebx,2 ; Ins B 这两条指令彼此之间是无关的,所以可以重新排序,先执行 Ins B 和先执行 Ins A 的结果是一样的,所以 CPU 为了速度,可能会先执行 Ins B 再执行 Ins A. 这就是重排列。而若是 mov [eax],1 mov ebx,[eax] 这样子的话,那就不能重排列了,因为两条指令是相关的. 由此,我们就可以成功通过让 CPU 对指令重排序,从而加快 CPU 的执行速度。 2 问题: 缓存不一致和内存执行顺序的不一致 2.1 CPU 乱序执行在多核心下的不一致性 乱序执行后,CPU 单核性能确实提上来了。然而,工程师设计之初可没想到有多核 -- 它只保证单核心最终的结果正确。我们来看另外一个情况, 就是书中的 3.2: 此时此刻,C2 核心中的 r1 和 r2 变量,可没有什么关系啊,那我把 L2 移动到 L1 前面可不可以? 在只有 C2 的情况下,这可一点问题都没有啊! 但是问题是,我们将 C1 核心和 C2 核心连起来看?诶,很显然,把 L2 丢上面是违背我们原来想要的逻辑的。所以,L1 和 L2 顺序是不能换的。 而这个情境,其实就是后续会涉及到的 Load-Load 重排序,在此先提前涉及一下。 那如何解决这个问题呢?我们必须建立起一套秩序,尽量减少甚至杜绝这类问题。 2.2 缓存带来的不一致性 既然都提到缓存了,那就展示一下书上所写的缓存的架构吧。 我们可以看到,科学家远比我们想象的要丧心病狂,他们给 CPU 加了很多很多的缓存。我们也可以看到,每个核心都会有一个私有缓存。 [!IMPORTANT] 在接下来的文章中,我们只关注私有缓存部分,我们将 LLC(L3 Cache) 和内存块看作一个东西,不加以区分。 而前面讲到了,我们实现缓存的目的有两个: 让缓存相对于程序员透明。 提升CPU访问主存的速度。 当然,书中有句老话说得好: 所以我们也必须得保证它的确定性。 现在,每个核心都有一个私有缓存,那每个核心之间的私有变量肯定是会出现不同步的。但是不同步就意味着会出错,连正确性都无法保证。 还有,就是如果缓存必须和内存保持强一致性的话,那不就和主存访问一样了吗,那第二个目的就搞不定了,缓存增加了个寂寞。
阅读全文