4、内存一致性模型:顺序一致性(SC)、总存储顺序(TSO)、弱一致性模型

好,咱们今天聊一个硬核话题——内存一致性模型。说实话,我刚入行那会儿,觉得这玩意儿就是个理论概念,跟实际设计关系不大。直到有一次,我调试一个四核芯片的数据竞争问题,折腾了整整两周,最后发现是内存序搞的鬼。从那以后,我再也不敢小看它了。

内存一致性模型,说白了就是定义了一个规则:多个处理器同时读写共享内存时,你看到的顺序应该是什么样的?你想想看,如果每个核都按自己的节奏来,那不乱套了吗?

4.1 顺序一致性(SC)——最直观,也最慢

顺序一致性是最简单的模型。它的核心思想就一句话:所有核的访存操作,看起来就像在单个全局时钟下串行执行。

嗯,这里要注意,我说的是「看起来像」,不是真的串行。实际上硬件还是并行的,只是结果必须跟串行一致。

SC 的规则:

  • 所有核的访存操作,必须按照程序顺序执行
  • 所有核看到的全局顺序必须一致
  • 写操作立即可见

举个例子,假设两个核同时跑这段代码:

// 初始状态:x = 0, y = 0
// 核0:            // 核1:
x = 1;              y = 1;
r1 = y;             r2 = x;

在SC模型下,最终结果只能是 (r1=0, r2=0)、(r1=1, r2=0)、(r1=0, r2=1) 或 (r1=1, r2=1) 这四种之一。绝对不会出现 r1=0 且 r2=0 同时为真的情况——因为总有一个写操作先发生。

我个人习惯在早期设计阶段先用SC模型做验证。为什么?因为它的行为最可预测,调试起来省心。但代价也很明显——性能差。每个访存操作都要等全局确认,流水线经常被卡住。

我的经验:SC模型适合核数少(2-4核)且对实时性要求高的场景。比如工业控制芯片,我见过不少用SC的。

4.2 总存储顺序(TSO)——SPARC和x86的选择

TSO是SC的「放松版」。它允许读操作越过写操作,但写操作之间必须保持顺序。

为什么会这样?说白了,是为了性能。你想想看,如果每次写都要等全局确认,那写缓冲就废了。TSO允许写缓冲存在,读操作可以先从缓冲里拿数据,不用等写操作真正刷到内存。

特性 SC TSO
写-写顺序 严格保持 严格保持
读-写顺序 严格保持 读可越过写
写-读顺序 严格保持 读可越过写
性能
实现复杂度

我在项目中遇到过一个问题:用x86平台跑一个多线程数据聚合程序,结果每次跑出来的结果都不一样。后来发现是TSO模型下,一个核的写操作被另一个核的读操作「跳过去了」。解决方案是加一个mfence指令,强制刷新写缓冲。

避坑指南:我曾经在移植代码时,把x86上的TSO假设直接带到了ARM平台上,结果死得很难看。不同架构的内存模型差异很大,千万别想当然。

4.3 弱一致性模型——性能至上

弱一致性模型,说白了就是「爱怎么乱序就怎么乱序,只要你能保证同步点正确」。它只要求在同步操作(比如锁的获取和释放)时保证一致性,其他时候随便折腾。

这个模型在ARM和RISC-V里很常见。为什么?因为现代处理器核数越来越多,如果每个访存都要全局同步,那性能就完蛋了。

弱一致性模型的规则很简单:

  • 同步操作之间,访存顺序可以任意重排
  • 同步操作必须按顺序执行
  • 同步操作之前的所有访存,必须在同步之前完成

举个例子:

// 核0:
data = 100;          // 普通写
flag = 1;            // 同步写(释放锁)

// 核1:
while (flag != 1);   // 同步读(获取锁)
r1 = data;           // 普通读

在弱一致性模型下,核0的 data=100 和 flag=1 可能乱序执行。但因为有同步操作,核1在读到 flag=1 之后,一定能看到 data=100。这就是同步点的作用。

关键点:弱一致性模型把保证一致性的责任推给了程序员。你必须在正确的地方插入内存屏障(memory barrier),否则程序就等着出bug吧。

我建议初学者先从SC模型理解起,然后再看TSO和弱一致性。为什么?因为SC是最直观的,理解了SC,你才能明白TSO「放松」了什么,弱一致性又「弱」在哪里。

4.4 三种模型的对比与选择

好了,咱们总结一下。三种模型的核心区别在于:

  • SC:严格按程序顺序执行,性能差,但调试简单
  • TSO:读可越过写,性能中等,x86和SPARC在用
  • 弱一致性:全靠同步点,性能最好,ARM和RISC-V在用

我个人习惯这样选:

  • 如果核数少(≤4),且对实时性要求高,用SC
  • 如果兼容x86生态,用TSO
  • 如果追求极致性能,且团队有经验,用弱一致性

一个小技巧:设计芯片时,可以在硬件层面支持多种一致性模型,通过配置寄存器切换。这样同一个芯片既能跑实时应用,又能跑高性能计算。我上一款芯片就是这么干的,效果不错。

最后说一句:内存一致性模型没有银弹。每种模型都有它的适用场景,关键是你得理解它的代价。嗯,今天就聊到这儿,下一节咱们讲缓存一致性协议,那又是另一个坑了。