x86不需要对齐lock cmpxchg
指令就可以成为原子指令。但是,对齐对于良好的性能是必要的。
这应该不足为奇,向后兼容性意味着使用 14 年前手册编写的软件仍然可以在今天的处理器上运行。现代 CPU 甚至有一个专门用于分割lock
检测的性能计数器,因为它非常昂贵。(核心不能只在操作期间保持对单个高速缓存行的独占访问;它确实必须做一些类似于传统总线锁的事情)。
为什么 Microsoft 记录对齐要求的确切原因尚不清楚。这对于支持 RISC 架构当然是必要的,但在多处理器 x86 上的不可预测行为的具体声明甚至可能无效。(除非它们意味着不可预测的性能,而不是正确性问题。)
您对仅适用于 486 之前的系统的猜测lock cmpxchg
可能是正确的;那里需要一种不同的机制,这可能需要围绕纯负载或纯存储进行某种锁定。(另请注意,486与现代()cmpxchg
具有不同且当前未记录的操作码(0f a7
),后者是 586 Pentium 的新功能;Windows 可能仅在 P5 Pentium 和更高版本上使用过,我不知道。)这也许可以解释一些 x86,但并不暗示现代 x86 的怪异。cmpxchg
0f b1
cmpxchg
英特尔® 64 和 IA-32 架构软件开发人员手册
第 3 卷 (3A):系统编程指南
2013 年 1 月
8.1.2.2 软件控制的总线锁定
为了显式地强制 LOCK 语义,软件可以在使用 LOCK 前缀和以下指令来修改内存位置时使用它们。[...]
• 交换指令(XADD、CMPXCHG 和 CMPXCHG8B)。
• XCHG 指令自动使用LOCK 前缀。
• [...]
[...] 总线锁的完整性不受内存字段对齐的影响。LOCK 语义遵循更新整个操作数所需的尽可能多的总线周期。但是,建议锁定访问在其自然边界上对齐,以获得更好的系统性能:
• 8 位访问的任何边界(锁定或其他)。
• 锁定字访问的 16 位边界。
• 锁定双字访问的 32 位边界。
• 锁定四字访问的 64 位边界。
有趣的事实:cmpxchg
没有前缀仍然是lock
原子的。上下文切换,因此可用于单核系统上的多线程。
即使未对齐,它仍然是原子的。中断(完全之前或完全之后),并且只有其他设备(例如 DMA)读取的内存可以看到撕裂。但是这样的访问也可以看到加载和存储之间的分离,因此即使旧 Windows 确实使用它来在单核系统上实现更高效的 InterlockedCompareExchange,它仍然不需要对齐以确保正确性,只需要性能。如果这可以用于硬件访问,Windows 可能不会这样做。
如果库函数需要执行与 this 分开的纯加载lock cmpxchg
可能是有意义的,但它不需要这样做。(如果没有内联,32 位版本必须从堆栈中加载其参数,但这是私有的,不能访问共享变量。)