第五章:Relaxed Memory Consistency

chapter5:宽松的内存一致性

之前所讨论的SC和TSO需要对所使访存顺序符合程序顺序,称为强一致性模型。而目的在于只保留程序员真正需要的顺序的弱一致性模型,可以允许更多的硬件或软件优化(编译器或运行时)减少排序约束,进一步提升性能。

1、为什么需要宽松内存一致性


程序员总是期望以下执行顺序

  • S1 -> S3 -> L1 loads SET -> L2
  • S2 -> S3 -> L1 loads SET -> L3

除此之外,TSO和SC还会保留S1和S2、L2和L3之间的顺序。

上图描述了一个更普遍的情况,即两个critical section(以下简称CS)使用同一个锁进行交接,其正确性由以下顺序进行保证:

  • All L1i, All S1j -> R1 -> A2 -> All L2i, All S2j

其正确性并不取决于CS内的load和store的顺序(除非load和store的地址相同),只需要保证先执行CS1再执行CS2。在这种情况下,load和store可以按任意顺序执行,或许可以通过放松load和store之间的顺序来获取更高的性能。

一个宽松的内存模型,可以对任何内存操作进行重排,并由FENCE指令进行必要的排序。这种模型需要程序员手动确定哪些操作需要排序可能比较麻烦,但也可以进行很多优化以提高性能。

常见的优化方法

不基于FIFO的合并式Write Buffer

在TSO中我们要求write buffer必须是基于FIFO的,但一个更优化的设计是使用不基于FIFO的write buffer,且允许同一地址或相邻地址的多个store请求合并为一个store。

对乱序执行的core支持更简单

在强一致性模型的系统中,一个内核可能在指令提交之前推测性地执行不按程序顺序的load,如MIPS R10000,但这样需要额外的检查流程,如MIPS R10000。相比之下,在宽松一致性模型中,乱序load符合模型要求,不需要额外的检查。

耦合的内存一致性与缓存一致性

宽松的一致性模型允许两个线程逻辑上共享一个核的write buffer等行为,容易形成一些核心load新值而另一些核心load旧值的情况出现,这短暂地打破了缓存一致性需要的“SWMR”不变量。因而需要特殊的、与一致性模型紧耦合的缓存一致性协议配合,这通常能够增强系统性能,但同时也带来相当大的验证复杂性。

2、弱一致性模型示例(Relaxed Consistency model,XC)

XC基本思想

XC提供了一个FENCE指令,在没有FENCE指令的情况下,load和store都可以是乱序的。FENCE指令不指定地址,同一个核的FENCE指令之间严格顺序,但FENCE不影响其他内核的内存指令顺序。

XC访存顺序保证以下程序顺序

  • Load -> FENCE
  • Store -> FENCE
  • FENCE -> FENCD
  • FENCE -> Load
  • FENCE -> Store

在操作相同地址时,采用TSO相同的规则

  • Load→Load to same address
  • Load→Store to same address
  • Store→Store to same address

对同一地址的顺序约束还是有必要的,比如Store -> Store规则可以避免出现先执行A=1再执行A=2但结果是A=1的情况,或者Load -> Load规则可以避免出现一开始A=0且有一个处理器写入A=1时,另一个处理器上第一次load A结果为1但是第二次load A结果为0的情况。

XC保证了load立即看到自己的store请求,类似TSO的write buffer bypassing,这样保证了单线程的顺序性。

XC中使用FENCE

XC形式化描述

所有内核都按如下规则将load、store和FENCE插入内存顺序<m:

  • 若L(a) <p FENCE,则L(a) <m FENCE
  • 若S(a) <p FENCE,则S(a) <m FENCE
  • 若FENCE <p FENCE,则FENCE <m FENCE
  • 若FENCE <p L(a),则FENCE <m L(a)
  • 若FENCE <p S(a),则FENCE <m S(a)

所有内核都将相同地址的load和store请求按如下顺序插入内存顺序<m:

  • 若L(a) <p L'(a),则L(a) <m L'(a)

  • 若L(a) <p S(a),则L(a) <m S(a)

  • 若SL(a) <p S'(a),则S(a) <m S'(a)

每一次load都从之前最后一次相同地址的store中获取值:

  • L(a)的值为MAX_<m

执行案例




3、XC实现

由reorder模块替换TSO中基于fifo的write buffer

  • 重排序单元(reorder unit)与core分开,loads、stores、FENCEs遵循程序顺序离开core并进入reorder unit的尾部。
  • reorder unit根据相关排序规则对消息进行排序并送至reorder unit的头部。
  • 当头部指令可以执行时((a)图中为被switch选中,(b)图中为L1缓存中的块有对应的权限)在头部执行load/store。

遵循以下规则

  • FENCE:FENCE的实现方法有很多,如1)按照SC模型排序,则FENCE看作no-ops;2)等待所有FENCE前的指令执行,全部执行完毕后再执行FENCE后的指令;3)更加激进的实现方式。但无论哪种实现都不应该重排序load→FENCE, store→FENCE, FENCE→ FENCE, FENCE→load, FENCE→store。
  • 对于同一地址的访存操作,reorder unit不能乱序 load→load, load→store, store→store.
  • load立即看到由于自身的store引发的更新。

实现原子指令

RMW的实现也取决于系统如何实现XC。在本节中,我们假设XC系统由乱序内核组成,每个内核通过一个不基于FIFO的合并式write buffer与内存系统相连。

一个简单方案是借用在TSO中讨论过的实现方式,在执行一个原子指令之前,清空write buffer,然后获得缓存块的读写权限(如M状态),然后进行load和store。但这种方案过于保守,牺牲了一部分性能。我们事实上不需要清空write buffer,因为XC允许RMW的load和store可以直接bypass之前的store

4、无数据争用程序的顺序一致性(Sequential Consistency For Data Race Free(DRF) Programs)

对于内存一致性模型来说同时具备SC模型的直观性和XC模型的高性能似乎是不可能的。但是对于无数据竞争程序而言是可以同时满足这两个目标的。数据竞争的概念是,当两个线程访问同一个内存位置,且至少有一个访问是store,且中间没有同步操作。通常数据竞争是编程错误导致的,许多程序员期望写无数据竞争的程序。

表5.7中,Core2没有进行同步操作,因而其load有可能和Core C1的store同时执行。由于XC模型允许Core1 S1,S2之间乱序,Core2 L1,L2之间乱序,则对应(r1, r2)会有四种输出结果:(0, 0), (0,NEW), (NEW, 0), (NEW, NEW)。

表5.8中,Core2进行了与Core1一样的acquire lock,这使得其critical section会完全先于(或后于)Core1的critical section。在这种情况下有两种可能输出结果:(r1, r2)=(0, 0), (NEW, NEW),尽管允许C1、C2都分别乱序执行S1、S2,L1、L2,但此时XC模型与SC模型输出结果是一致的。原因就是在C1执行乱序store时没有concurrent load被执行。

从上面的例子中我们可以看出,插入必要的FENCE之后,XC的结果与SC的结果是完全一样的。

  • 如果存在数据竞争,那么就会暴露XC对load和store的重排。
  • 如果不存在数据竞争,那么XC和SC执行结果没有区别。

为了对SC for DRF理解更深,需要引入以下定义:

  • synchronization:一种内存操作,包括lock acquire和lock release。一些内存操作被标记为“synchronization operations”,与之相对则是“data operations”

  • conflict:conflict有两种情况。

    • 第一种,来自不同核,或多线程处理器的不同线程的两个data operation Di和Dj,它们相同的内存地址,并且最少有一个operation是store。
    • 第二种,来自不同核,或多线程处理器的不同线程的两个synchronization操作Si和Sj访问了相同的内存地址,并且最少有一个operation是write(如spinlock的acquire和release)。
  • transive conflict:如果两个synchronization operation Si和Sj冲突,或者Si与Sk冲突,Sk <p Sk',Sk‘与Sj冲突,则Si与Sj构成transive conflict。

  • race:如果两个data operation产生conflict,并且它们的global memory顺序上没有一对来自各自处理器核(或线程)相干的transive conflict synchronization operation,那么这两个data operation构成竞争。换句话说,对于一对conflict的data operation Di <m Dj,当且仅当有一对相干的synchronization operation Si, Sj使得Di <m Si <m Sj < m Dj时,不会产生数据竞争。

  • 如果没有data operation竞争,那么一条SC的指令执行是data-race-free(DRF)的

  • 如果程序中所有SC的指令执行都是DRF的,那么这个程序是DRF的

  • 如果所有DRF程序的所有指令执行都是SC指令执行,那么称这个内存一致性模型支持“SC for DRF programs”

如上图所示,很难准确描述哪些内存操作可能发生数据竞争,因而必须保守地标记同步操作,插入FENCE。C2的“X=2”前后的FENCE当且仅当C1执行“X=1”的时候是被需要的,而判断出在C2执行“X=2”时C1是否执行“X=1”取决于C1之前的程序执行延时情况,这是不确定的。我们固然可以在所有不确定的情况下都插入FENCE,但是这会损伤性能。

5、一些宽松内存模型的概念

释放一致性(Release Consistency)

一个synchronization acquire只需要在其之后插入FENCE,一个synchronization release只需要在其之前插入FENCE即可。

如图5.4中,F11、F14、F21和F24可以省略。以C1的释放锁为例,F13不能省略,但F14可以,因为C1后续的内存操作在释放锁之前执行也是没有问题的。XC不允许有内存指令绕过FENCE,但RC允许这样的提前执行。事实上,RC提供了类似于FENCE的ACQUIRE和RELEASE指令,但只在一个方向上对内存访问进行排序,不同于在往前和王后两个方向进行排序的FENCE。

因果性和写入原子性

因果性(causality)在一致性模型中要求,如果一个核看到了一个写入结果并告诉了另一个核,那么另一个核也看到了这个结果。如表5.9中,如果r3=NEW,那么就保证了系统的causality。

写原子性(write atomicity)在内存一致性中要求,一个处理器核的store逻辑上立即被所有其它处理器核看到。同时,write atomicity允许store被自身处理器核先于其它处理器核看到。XC模型实际存在的 <m 内存顺序决定了其具备write atomicity的特性:在这之前,没有其它核看到了新值;在这之后,所有核都看到了新值。

posted @ 2024-04-30 15:53  骑猪上树的少年  阅读(3)  评论(0编辑  收藏  举报
回到顶部