x86-64环境下伪共享与字节对齐引发的性能异常问题咨询
问题解答
核心原因是x86 CPU默认开启的相邻缓存行硬件预取机制触发了隐性伪共享,导致c被a/b的缓存一致性流量牵连。
缓存行地址计算
x86-64架构CPU的标准缓存行大小为64字节,对第一次测试的变量地址做缓存行对齐计算:
# a/b 所在缓存行范围:0x406200 ~ 0x40623F 0x406200 & ~(64-1) = 0x406200 # c 所在缓存行范围:0x406240 ~ 0x40627F 0x406258 & ~(64-1) = 0x406240 # d 所在缓存行范围:0x406280 ~ 0x4062BF 0x406298 & ~(64-1) = 0x406280
可以看到:a/b的缓存行和c的缓存行是相邻的前后行,d的缓存行和a/b的缓存行间隔了一个完整的64字节缓存行。
性能差异原因
x86 CPU为了提升顺序访问场景的性能,默认开启相邻缓存行预取:当某个缓存行被高频访问时,硬件会自动将它的下一个相邻缓存行预取到访问核心的私有缓存中。
第一次测试的运行流程触发了非预期的缓存一致性流量:
- thread1、thread2高频修改
a/b所在的缓存行,硬件预取器自动将相邻的c所在缓存行预取到运行thread1、thread2的核心缓存中,状态为共享 - thread3每次修改
c时,都需要发送RFO(读所有权)请求,将其他核心中c所在缓存行的状态置为无效,才能获得独占修改权限 - 这个过程产生了大量不必要的缓存一致性开销,属于预取引入的隐性伪共享,所以thread3的执行速度大幅变慢
d的缓存行和a/b的缓存行间隔了64字节,不会被预取逻辑牵连,所以thread4的运行速度符合预期
第二次测试符合预期的原因
将a/b改为int64_t后,变量整体内存布局偏移,a/b所在的缓存行和c所在的缓存行不再是相邻关系,中间至少间隔了一个完整缓存行,预取逻辑不会把c的缓存行预取到运行thread1、thread2的核心中,因此c不会被a/b的伪共享流量牵连,结果符合你的预期。
验证方案
你可以通过以下操作验证上述结论:
- 关闭CPU的相邻缓存行预取功能后重跑第一次测试,thread3和thread4的耗时会基本一致
- 把第一次测试中的
p1数组长度从7改为15,让c和a/b的缓存行间隔至少128字节,thread3的耗时也会降到和thread4接近
内容的提问来源于stack exchange,提问作者Ryan Gao
相关产品推荐
相关产品推荐

