是的,避免失败重试路径内的获取障碍的总体想法可能是有用的,尽管如果您只是旋转,则失败情况下的性能几乎不相关。pause
or yield
节省电力。在 x86 上,pause
还提高了 SMT 友好性,并避免在另一个核心修改了您正在旋转的内存位置后离开循环时内存顺序错误推测。
但这就是为什么 CAS 有单独的memory_order
成功和失败的参数。宽松的失败可以让编译器仅在离开循环路径上设置障碍。
atomic_flag
test_and_set
但没有这个选项。手动执行此操作可能会损害像 AArch64 这样的 ISA,这些 ISA 本来可以获取 RMW 并避免显式的栅栏指令。 (例如与ldarb
)
Godbolt:原始循环与lock.test_and_set(std::memory_order_acquire)
:
# AArch64 gcc8.2 -O3
.L6: # do{
ldaxrb w0, [x19] # acquire load-exclusive
stxrb w1, w20, [x19] # relaxed store-exclusive
cbnz w1, .L6 # LL/SC failure retry
tst w0, 255
bne .L6 # }while(old value was != 0)
... no barrier after this
(是的,它看起来像是一个错过的优化,它只测试低 8 位tst
而不仅仅是cbnz w1, .L6
)
while(宽松的RMW) +std::atomic_thread_fence(std::memory_order_acquire);
.L14: # do {
ldxrb w0, [x19] # relaxed load-exclusive
stxrb w1, w20, [x19] # relaxed store-exclusive
cbnz w1, .L14 # LL/SC retry
tst w0, 255
bne .L14 # }while(old value was != 0)
dmb ishld #### Acquire fence
...
对于 32 位 ARMv8 来说更糟 where dmb ishld
不可用,或者编译器不使用它。你会得到一个dmb ish
全屏障。
Or with -march=armv8.1-a
.L2:
swpab w20, w0, [x19]
tst w0, 255
bne .L2
mov x2, 19
...
vs.
.L9:
swpb w20, w0, [x19]
tst w0, 255
bne .L9
dmb ishld # acquire barrier (load ordering)
mov x2, 19
...