“多核中由其他核心写权限引起的失效,需要另查缓存一致性;其中对不同变量的同块争用是伪共享。把这些缺失都归为“访问顺序差”,会把优化方向带偏。”
形式陈述
若不同核心访问逻辑上不共享、至少一方会写的不同数据,却因这些数据落在同一一致性块内而触发额外权限失效和传输,称为伪共享。典型情形是两个核心反复写各自计数器,但两个计数器共占一条缓存行。
“伪”修饰的是程序数据共享:变量彼此独立;块级权限争用则是真实发生的。只读同块通常可以共同持有读权限,不产生这里的反复写权转移。仅凭两个变量相邻,不能断言已经存在有害伪共享,还要看访问主体、读写模式和实际块边界。
直觉
写权限按整行发放,程序员却常按单个变量思考。两人虽然各自在纸的不同半边写字,协议仍要求整张纸一次只给一个写者,纸便来回传。
隔开变量可以减少这种传递,但会增加占用空间,也可能破坏有用的空间局部性。因此应对确实争用的独立热数据隔离,而不是把每个字段都盲目填满一行。
例子与边界
六次写,六次所有权请求
用本文原子总线MSI计数,块大小64 B、两个私有cache、冷启动,不发生容量逐出。P只写地址0x00的四字节变量x,Q只写0x04的四字节变量y。操作次序为 P:x=1, Q:y=1, P:x=2, Q:y=2, P:x=3, Q:y=3。
两地址属于同一块。第一次P从I发BusRdX取得M;随后每次换核心写,旧M供出含双方最新字段的块并失效,新核心发BusRdX取得M。共6次独占请求,其中5次从旧M转交;本变体这5次还更新内存。最后Q持M,完整块含x=3、y=3,内存中的y仍为2,需末尾回写才能更新。
这里每次只修改自己的四字节,但转交的是整块。若新写者只取得自己的字段而丢弃另一字段,就会产生丢失更新,那已经是协议错误,不只是伪共享性能问题。
改布局,不改算法
把y放到0x40,确保两个地址分属不同64字节块。仍按完全相同次序执行六次写,且假设两块分别能驻留各自cache。P第一次写x、Q第一次写y各发一次BusRdX,后四次均为本地M命中,共2次独占请求,没有核心间块所有权交接。
若要把最终值都送到内存,仍需分别回写两个M块;不能把“无需互相转交”误读成“无需任何传输”。实验须比较相同的结束条件。
伪共享不等于数据竞争
P与Q各自访问不同变量,可以没有同一语言对象上的冲突访问,仍承受上述权限往返。反过来,两线程无同步写同一个普通变量可能存在数据竞争,即使恰好只在一个核心轮流执行、没有跨核心行乒乓。语言正确性和块布局性能不是同一判断。
推论与应用
MCS队列锁与CLH锁的通信分析常需要说明节点或轮询字段的缓存行隔离假设。逻辑上每人轮询独立字段,并不保证硬件中独立的行流量。
要验证一个布局改动,应保留算法、操作数和同步语义,记录地址到块的映射,再比较指定协议下的权限事务。真实机器的性能评估还须测量调度、行大小、预取与拓扑;本页6对2的结果只是固定模型的事务计数。
参考资料
- Vijay Nagarajan et al., A Primer on Memory Consistency and Cache Coherence, 2nd ed., 2020,§9.2.2,伪共享及块粒度。
- William J. Bolosky and Michael L. Scott, “False Sharing and Its Effect on Shared Memory Performance,” USENIX Symposium on Experiences with Distributed and Multiprocessor Systems, 1993;不同数据共块的性能问题。本文两种布局的事务计数严格使用本文MSI变体。