37

Microsoft 提供了InterlockedCompareExchange执行原子比较和交换操作的功能。还有一个内在的。_InterlockedCompareExchange

在 x86 上,这些是使用lock cmpxchg指令实现的。

但是,通过阅读有关这三种方法的文档,他们似乎并没有就对齐要求达成一致。

英特尔的参考手册没有提到对齐(除了如果启用对齐检查并进行未对齐的内存引用,则会生成异常)

我还查找了lock前缀,其中明确指出

LOCK 前缀的完整性不受内存字段对齐的影响。

(强调我的)

所以英特尔似乎说对齐是无关紧要的。无论如何,操作都是原子的。

_InterlockedCompareExchange内在文档也没有说明对齐,但是该InterlockedCompareExchange 函数指出

此函数的参数必须在 32 位边界上对齐;否则,该函数将在多处理器 x86 系统和任何非 x86 系统上运行不可预测。

那么给了什么?对齐要求是否InterlockedCompareExchange只是为了确保该功能即使在cmpxchg指令不可用的 486 之前的 CPU 上也能正常工作?根据上述信息,这似乎很可能,但在我依赖它之前我想确定一下。:)

或者ISA是否需要对齐来保证原子性,而我只是在英特尔的参考手册中寻找错误的地方?

4

4 回答 4

11

x86不需要对齐lock cmpxchg指令就可以成为原子指令。但是,对齐对于良好的性能是必要的。

这应该不足为奇,向后兼容性意味着使用 14 年前手册编写的软件仍然可以在今天的处理器上运行。现代 CPU 甚至有一个专门用于分割lock检测的性能计数器,因为它非常昂贵。(核心不能只在操作期间保持对单个高速缓存行的独占访问;它确实必须做一些类似于传统总线锁的事情)。

为什么 Microsoft 记录对齐要求的确切原因尚不清楚。这对于支持 RISC 架构当然是必要的,但在多处理器 x86 上的不可预测行为的具体声明甚至可能无效。(除非它们意味着不可预测的性能,而不是正确性问题。)

您对仅适用于 486 之前的系统的猜测lock cmpxchg可能是正确的;那里需要一种不同的机制,这可能需要围绕纯负载或纯存储进行某种锁定。(另请注意,486与现代cmpxchg具有不同且当前未记录的操作码0f a7),后者是 586 Pentium 的新功能;Windows 可能仅在 P5 Pentium 和更高版本上使用过,我不知道。)这也许可以解释一些 x86,但并不暗示现代 x86 的怪异。cmpxchg0f b1cmpxchg

英特尔® 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 位版本必须从堆栈中加载其参数,但这是私有的,不能访问共享变量。)

于 2013-03-20T12:11:06.943 回答
9

您引用的PDF是 1999 年的,显然已经过时了。

最新的英特尔文档,特别是第 3A 卷讲述了一个不同的故事。

例如,在 Core-i7 处理器上,您仍然必须确保您的数据不会跨越缓存线,否则不能保证操作是原子的。

在第 3A 卷,系统编程,对于 x86/x64 英特尔明确指出:

8.1.1 保证原子操作

Intel486 处理器(以及之后的更新处理器)保证以下基本内存操作将始终以原子方式执行:

  • 读取或写入一个字节
  • 读取或写入在 16 位边界上对齐的字
  • 读取或写入在 32 位边界上对齐的双字

Pentium 处理器(以及之后的更新处理器)保证以下额外的内存操作将始终以原子方式执行:

  • 读取或写入在 64 位边界上对齐的四字
  • 对适合 32 位数据总线的未缓存内存位置进行 16 位访问

P6 系列处理器(以及之后的更新处理器)保证以下附加内存操作将始终以原子方式执行:

  • 对适合高速缓存行的高速缓存内存进行未对齐的 16、32 和 64 位访问

Intel Core 2 Duo、Intel® Atom™、Intel Core Duo、Pentium M、Pentium 4、Intel Xeon、P6 系列、Pentium 和Intel486 处理器。Intel Core 2 Duo、Intel Atom、Intel Core Duo、Pentium M、Pentium 4、Intel Xeon 和 P6 系列处理器提供总线控制信号,允许外部存储器子系统使分离访问原子化;但是,不对齐的数据访问会严重影响处理器的性能,应该避免

于 2011-03-03T09:37:45.250 回答
4

请参阅这个 SO 问题:自然对齐对性能很重要,并且在 x64 架构上是必需的(因此不仅是 PRE-x86 系统,还有 POST-x86 系统——x64 可能仍然是一个小众案例,但它正在增长毕竟很受欢迎;-); 这可能是微软根据需要记录它的原因(很难找到关于 MS 是否决定通过启用对齐检查来强制对齐问题的文档——这可能因 Windows 版本而异;通过在文档中声明需要对齐,MS 保留自由在某些版本的 Windows 中强制它,即使他们没有在其他版本上强制它)。

于 2009-09-12T15:15:30.423 回答
3

Microsoft 的 Interlocked API 也适用于 ia64(虽然它仍然存在)。ia64 上没有锁定前缀,只有 cmpxchg.acq 和 cmpxchg.rel 指令(或 fetchadd 和其他类似的小动物),如果我没记错的话,这些都需要对齐。

于 2009-11-25T23:01:47.780 回答