memory-consistency-model

本文介绍了memory consistency model。

Introduction

首先,我们要区分cache coherency和memory consistency两个概念,cache coherency保证了多个处理器看到的存储内容是一致的。但它并没有回答这些存储器内容应当保持何种程度的一致性。这里“何种程度的一致性”,也就是一个处理器必须在什么时候看到另一个处理器更新过的值,而处理器要看到,唯一的方法就是读取,所以问题变成:在不同处理器对不同位置执行读取和写入操作时,必须保持哪些特性?memory consistency就是回答这个问题的。

举个例子来说明会更好理解。比如一段程序,P0往A地址里面写0,P1往B地址里面写0,然后一段时间之后P0去读B,P1去读A。假如... 的时间足够长,P0一定能够读到B的值是0,P1一定能够读到A的值是0。因为P0和P1操作的是同一个存储器,coherency要求P0和P1必须能够观察到这个存储器的值,并且他们观察到的是一致的。或者换一种说法,coherency主要是针对有cache的情况,如果系统中没有cache,两个处理器都是直接连在memory上的,那它天然就是遵循coherency的,因为他们始终都观察到同一个存储器,也就保证了一致性。只有在有cache的系统里,才可能出现coherency的问题。

1
2
3
4
 P0              |  P1
A=0; | B=0;
... | .....
if(B==0) | if(A==0)

但是呢,coherency没有规定A和B什么时候被写进memory,尤其是当读写操作变多时,每笔操作的顺序coherency并不保证。

再来看一个段程序。假设写入操作总是立即写入memory,那么在两个if判断的时候必然不可能同时为真。然而,假设A=1和B=1这笔操作被存在了store buffer里面,还没有往存储器写,相当于读取A和B的动作和前一笔写的动作在存储器看来是被颠倒了,那两个if判断就可能同时为真。memory consistency要回答的就是这个问题,到底允许不允许这些情况的发生。前面说的这两种情况,就是两种memory consistency model。

1
2
3
4
5
    P0              |  P1
1: A=0; | B=0;
2: ... | .....
3: A=1; | B=1;
4: if(B==0) | if(A==0)

sequential consistency

前面例子里的第一种情况,就是sequential consistency。它要求程序按顺序执行存储器访问操作,上述例子中A=1必须在B==0前面被执行。当然,不同处理器之间的访问可以相互交错在一起。也就是P0和P1的内部顺序必须是1,2,3,4。P0P1之间可以是P0-1,P1-1,P1-2,P0-2。在这种模型要求下,上述if条件是不可能出现同时为真的情况的。

可以看出,这种模型,强制要求了顺序,导致即使两笔操作的地址不同,也不能乱序执行,这导致后一笔操作必须等到前一笔操作完成之后才能执行,大大降低了性能,特别是当处理器数目很大或者延迟很长时,尤为如此。

relaxed consistency models

为了得到更快的硬件速度,relaxed consistency models出现了。它的关键思想是允许乱序执行读取和写入操作,使用同步操作来实施排序,从而表现得和sequential consistency一样。

relaxed consistency models是多种多样的,每种架构都有自己的做法。由于同步过程与处理器特性密切相关,而且很容易导致错误,所以才需要有标准同步库,编写同步程序,使程序员感觉不到是哪种模型,这可能也是一种新的处理器架构RISCV面临的很大的困难,必须为很多程序编写一套同步程序。

我们可以根据放松了哪种读取和写入顺序来分类。

sequential consistency: R->W、R->R、W->R、W->W。所有读写顺序都要保持。

total store ordering/ processor consistency: 放松W->R。由于保持了写入顺序,所以很多sequential consistency的程序也能在这一模型下运行,不用添加同步。

partial store order: 放松了W->R和W->W。

weak ordering/ release consistency: 放松了所有四种顺序。RISCV是属于这种。

放松了越多的限制,性能能够得到越多的提升,但是同步操作也会变得越复杂。这也是为什么不同架构的程序不一样的原因。

虽然release consistency放松了四种顺序,但还是定义了acquire和release来同步操作。定义SA为用acquire访问shared variable,定义SR为用release访问shared variable。

acquire的意思是:在acquire内存操作发生之前,没有后续内存操作可以被观察到。也就是说operation一定会被挡在acquire之后。

release的意思是:在任何更早的mem操作发生之前,release操作不能被观察到。也就是说,release不能提早到operation操作之前,也就是operation不能在release之后。

下图显示了几种model所规定的顺序。

上图:A是读,B是写,C是写,D是读,E是写,F是写。

sequential必须保证所有的顺序。

TSO,允许CD之间乱序,也就是允许W->R的乱序。

PSO,允许CD,EF之间乱序,也就是允许W->R,W->W乱序。

Weak,允许AB,CD,EF之间乱序,也就是允许R>W, W->R, W->W乱序(也允许R->R)。不允许任何操作越过acquire和release,也就是AB必须在acquire之前,CD必须在acquire之后,EF必须在release之后。

**

release,允许AB,CD,EF之间乱序,也就是允许R>W, W->R, W->W乱序(也允许R->R)。允许AB越过acquire,但是不能越过release,允许EF越过release但不允许越过acquire。

参考Computer Architecture A Quantitative Approach (6th Edition)