Appearance
第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 机器中,数据在哪个内存节点会影响延迟;线程迁移可能使原本本地的数据变成远端数据。
优化共享状态时,应同时测争用、缓存行转移和工作分配。减少锁次数但增加所有线程共同更新的计数器,不一定改善扩展性。
练习
- 解释伪共享与真正的数据竞争的区别。
- 给出以上双零结果在顺序一致性下不可能的顺序证明。
- 一个全局计数器为何可能使多核程序比单核还慢?