在 Intel 上,CMPXCHG 的参数必须与高速缓存行对齐(因为 Intel 使用 MESI 来实现 CAS)。
在 ARM 上,ldrex 和 strex 在独占保留粒度上运行。
需要明确的是,这是否意味着在 ARM 上,正在操作的数据不必与高速缓存行对齐?
独占访问限制
以下限制适用于独占访问:
• 具有给定ID 的独占写入的大小和长度必须与先前具有相同ID 的独占读取的大小和长度相同。
• 独占访问的地址必须与事务中的总字节数对齐。
• 独占读取和独占写入的地址必须相同。
• 独占访问的读取部分的ARID 字段必须与写入部分的AWID 匹配。
• 独占访问的读取和写入部分的控制信号必须相同。
• 在独占访问突发中传输的字节数必须是 2 的幂,即 1、2、4、8、16、32、64 或 128 字节。
• 独占突发中可以传输的最大字节数为 128。
• ARCACHE[3:0] 或AWCACHE[3:0] 信号的值必须保证监控独占访问的从机看到事务。例如,被从属设备监视的独占访问不能具有指示事务可缓存的 ARCACHE[3:0] 或 AWCACHE[3:0] 值。
不遵守这些限制会导致不可预测的行为。
以上来自 AMBA/AXI 规范。您会发现某些供应商忽略了 AWLOCK/ARLOCK(这意味着 ldrex/strex 无法在内核之外工作)。我有一些代码可以证明这一点,或者至少如果你发现一个不支持独占访问的系统会。
https://github.com/dwelch67/raspberrypi/tree/master/extest
根据任务和您想要的可移植性,您可能需要提供被 ifdefs 包围的 swp 和 ldrex/strex 解决方案和/或使用过多的可用寄存器(运行时)来告诉您内核支持或不支持哪些指令你正在运行。(在至少一种情况下,您可能会发现 swp 和 ldrex/strex 均不受支持)。
它在 ARM 架构参考手册 A.3.2.1 “未对齐的数据访问”中说得对。LDREX
并STREX
要求字对齐。这是有道理的,因为未对齐的数据访问可以跨越排他的预留粒度。
在 Intel 上,CMPXCHG 的参数不需要缓存对齐。试试看,你会发现它有效。
但是,您是对的:在可缓存内存中,英特尔确实使用缓存协议来实现 CMPXCHG。因此,最好不要将两个独立的高使用率同步变量放在同一个缓存行中——因为如果两个处理器使用这些不同的变量进行同步,缓存行可能会来回颠簸。但这与任何数据的问题完全相同:您不会让不同的处理器同时写入同一个高速缓存行。虚假分享。
但是你当然可以不缓存行对齐锁:
struct Foo {
int data;
Lock lock;
int data_after;
};
您可以将不同的锁放在同一个缓存行中:
struct Foo {
int data;
Lock read_lock;
int data_between;
Lock write_lock;
int data_after;
};
由于阅读和写作往往是互斥的,因此可能没有损失;
您可以将不同的锁放在同一个缓存行中:
struct Foo {
int data;
Lock read_lock;
int data_between;
Lock write_lock;
int data_after;
};
顺便说一句,在非缓存内存中,英特尔不会将缓存侦听协议用于 CMPXCHG 等原子操作。因此缓存行对齐同步变量的理由较少。但是您仍然可能希望:许多内存子系统按缓存线大小交错,即使在未缓存时也是如此。
至于 ARM:几乎是一样的。
在 snoopy 总线或未缓存的总线上,您可能不需要过多担心缓存行对齐。
但是在集群缓存层次结构中,您会遇到与 x86 完全相同的问题。更重要的是,事实上,众所周知如何“导出”CMPXCHG 之类的操作,而不是 ARM ldrexd/strexd。