论文:TreadMarks
本节我们以 TreadMarks 为模型聊一聊释放一致性的是使用场景。
所谓释放一致性,是指在对资源操作前,先进行 acquire 操作即获取锁,其实是对某个标示位的值进行读取,若该资源正在被其他进程操作,该标示位为 0,当资源操作完成时会向所有副本发出更新通知,所有副本更新成功后将改标示位改成1,即释放锁(release),而在此之前,后面的进程只能轮询该标示位以获取锁状态。可见这种一致性模型每次只将修改的部分通知大家更新一下,分散了一致性的压力,数据竞争较小的地方会有比较理想的性能提升。
但是,每次通知大家更新的时候是需要等所有人都回复OK后才能释放锁。如果 A 修改后不通知 B 和 C,BC 在每次 acquire 之前先把自己的数据更新一下,这样可以将 A 修改同步的负载延迟到各个副本上,这种策略叫 Lazy Release Consistency(LRC)。
TreadMarks 为分布式共享式内存而设计,采用 LRC 策略保证一致性,比 RC 提高了性能,但需要将资源的操作分成一个个 interval:acquire 和 release 之间,可理解为逻辑上的时钟,称为 vector clock,它维护了各个进程的资源竞争的因果关系,通过合并操作可以得出每个 interval 结束时的资源状态,保证数据的一致性。
在分布式共享内存中还有一个性能杀手,False Sharing。由于内存的管理单位是页,进程 A 修改的页1的部分1,而进程 B 读取页1的部分2,传统的方式会先把进程B页1副本更新后再进行读取,而进程 A 的修改对进程 B 的读取没有影响,这显然是可以优化的。TreadMarks 的优化策略是在每个 interval 中创建 diff,也就是说,进程 A 写入页1部分1之前先创建页1的复制 twin,然后再去页1中修改,当进程B读取页1时,比较要读的部分在页1和twin有没有区别,若没有则直接读本地页1副本,如有区别则创建创建 diff。根据 vector clock 在时间上因果关系推算出当前进程应该读到那个版本的diff,如此一来,将单一的数据页同步转化成状态的叠加,可以有效降低 False Sharing 的性能损耗,当然需要更多的空间存 diff,用空间换时间。
没有十全十美的解决方案,只有适合不同场景的平衡取舍,由于分布式内存资源竞争概率远小于非竞争的情况,所以在在竞争同步,创建 diff 的策略上采取 lazy 的方式。反过来说,如果内存竞争的情况高于非竞争的情况,这种处理方式反而会使内存的使用效率降低,比如单机。