x86 TSO下store buffer litmus测试命名由来与重排序成因疑问
核心测试用例:
Litmus Test: Write Queue (also called Store Buffer) Can this program see r1 = 0, r2 = 0? // Thread 1 // Thread 2 x = 1 y = 1 r1 = y r2 = x On sequentially consistent hardware: no. On x86 (or other TSO): yes!
核心结论先明确
store-load重排序是x86 TSO内存模型架构层面允许的行为规范,store buffer、OoO执行都是硬件层面实现该规范的可选技术路径,二者不存在因果关系,是平行的、可独立或共同作用的诱因。
具体原理拆解
1. store buffer 导致重排序的路径
该场景完全不需要乱序执行参与,在完全按序执行的CPU上也可复现:
- 线程1执行
x = 1时,写操作先写入本核私有的store buffer,没有立刻同步到全局缓存一致性域 - 线程1接下来执行
r1 = y,由于x和y是不同地址,TSO允许读操作绕过本核store buffer中未提交的写操作,直接向缓存发起请求,此时若线程2的y=1还未同步到缓存,r1就会读到0 - 线程2同理执行得到r2=0,最终出现二者同时为0的结果
这也是该测试被命名为store buffer测试的历史原因:最早在in-order的TSO架构上观察到该现象时,唯一的诱因就是store buffer。
2. OoO执行导致重排序的路径
即使没有store buffer,只要CPU支持乱序执行,且内存模型允许store-load重排序,也可复现该结果:
- 由于
x=1和r1=y两个操作没有地址依赖、也没有指令依赖,乱序调度器可以将r1=y的执行顺序提前到x=1之前 - 此时先读y拿到0,再执行x的写入,同样可以得到和store buffer场景完全一致的结果
注意x86的乱序执行受内存模型约束,只会允许符合TSO规范的store-load重排序,其他类型的重排序会被硬件内存序检查逻辑拦截。
x86上的结果诱因与区分方法
现代消费级x86 CPU同时具备store buffer和乱序执行能力,观测到的结果通常是二者共同作用的结果,可以通过以下两种方法区分二者的贡献:
- 方法1:在in-order架构的x86核(如低功耗嵌入式x86、早期80486及之前的x86)上运行测试,如果仍能复现
r1=0, r2=0的结果,则完全由store buffer导致,因为in-order核没有乱序执行能力 - 方法2:给读操作增加对写操作的地址依赖,强制禁止乱序调度,例如将线程1的代码修改为
r1 = y + (x * 0),此时读y的操作依赖于x的计算结果,乱序调度器无法将读操作提前到写x之前,若仍能复现结果,则可确认是store buffer的贡献
三点因果关系的澄清
- OoO执行确实可以引发内存重排序,但它只能触发内存模型允许的重排序类型,无法突破架构层面的内存序承诺
- store buffer是store-load重排序的经典、最早被发现的诱因,因此测试用它命名,属于历史命名惯例,不代表该重排序只能由store buffer导致
- 同一测试被用作OoO执行的示例,是因为在现代乱序执行CPU上,该重排序也可由OoO触发,二者并不冲突,只是不同实现路径产生了相同的架构层面可见结果
内容的提问来源于stack exchange,提问作者zanmato
相关产品推荐
相关产品推荐

