Skip to content

第6章:多核、一致性与存储模型

副本与写权限

多核各自缓存同一内存块时,一个核心写入后,其他核心的旧副本必须按协议处理。缓存一致性负责同一位置的写入传播和观察顺序;存储一致性模型规定不同位置的读写可以以什么顺序被观察。

简化 MESI 协议用 Modified、Exclusive、Shared、Invalid 表示缓存行状态。核心要写一个共享行,通常先取得独占写权限并使其他副本失效。具体消息、目录和瞬态状态远多于这四个名称。

两个线程修改同一缓存行中的不同变量,也可能反复转移整行所有权,称为伪共享。逻辑变量没有冲突,不代表物理传输粒度没有冲突。

顺序一致性与宽松模型

顺序一致性要求存在一个与各线程程序顺序一致的全局交错。硬件为了性能可能允许某些重排,语言内存模型又规定编译器与程序员应遵守的规则。

初始 x=y=0,线程 A 执行 x=1; r1=y,线程 B 执行 y=1; r2=x。若是顺序一致的原子读写,不可能同时得到 r1=r2=0:两次读都在对方写之前,会与各自先写后读共同形成顺序环。但带写缓冲的某些硬件模型可以允许该结果。

若在 C++ 中直接用没有同步的普通共享变量实现,这还可能构成数据竞争并导致未定义行为,不能用一个硬件实验替代语言层推理。

原子操作与同步

原子读改写把读取与更新结合为不可分割操作,是锁和并发数据结构的基础。互斥只说明某段代码不同时进入,锁还必须提供需要的可见性与顺序保证。

发布数据时,写者先写内容,再以 release 操作发布标志;读者用 acquire 操作读到相应发布值后,才能据语言规则看到之前的数据。普通标志变量、随意插入延时或 volatile 通常不能代替这个同步关系。

可扩展性

核心数增加后,串行部分、共享锁、内存带宽和跨插槽访问都会限制加速。NUMA 机器中,数据在哪个内存节点会影响延迟;线程迁移可能使原本本地的数据变成远端数据。

优化共享状态时,应同时测争用、缓存行转移和工作分配。减少锁次数但增加所有线程共同更新的计数器,不一定改善扩展性。

练习

  1. 解释伪共享与真正的数据竞争的区别。
  2. 给出以上双零结果在顺序一致性下不可能的顺序证明。
  3. 一个全局计数器为何可能使多核程序比单核还慢?

上次更新: