假设重复的获取操作,尝试加载或交换一个值,直到观察到的值是所需的值。
让我们以Cp偏好原子标志示例为起点:
void f(int n)
{
for (int cnt = 0; cnt < 100; ++cnt) {
while (lock.test_and_set(std::memory_order_acquire)) // acquire lock
; // spin
std::cout << "Output from thread " << n << '\n';
lock.clear(std::memory_order_release); // release lock
}
}现在,让我们考虑一下对这种旋转的增强。两个著名的例子是:
pause或yield,而不是非操作旋转.我能想到第三个,我想知道这是否有意义。我们可以使用std::atomic_thread_fence获取语义:
void f(int n)
{
for (int cnt = 0; cnt < 100; ++cnt) {
while (lock.test_and_set(std::memory_order_relaxed)) // acquire lock
; // spin
std::atomic_thread_fence(std::memory_order_acquire); // acquire fence
std::cout << "Output from thread " << n << '\n';
lock.clear(std::memory_order_release); // release lock
}
}我希望这对x86来说没有什么变化。
我想知道:
yield指令的决定有任何干扰?我不仅对atomic_flag::clear / atomic_flag::test_and_set对感兴趣,还对atomic<uint32_t>::store / atomic<uint32_t>::load对感兴趣。
可能改变为放松负荷可能是有意义的:
void f(int n)
{
for (int cnt = 0; cnt < 100; ++cnt) {
while (lock.test_and_set(std::memory_order_acquire)) // acquire lock
while (lock.test(std::memory_order_relaxed))
YieldProcessor(); // spin
std::cout << "Output from thread " << n << '\n';
lock.clear(std::memory_order_release); // release lock
}
}发布于 2020-06-11 11:40:10
是的,避免失败重试路径中的获取障碍的一般想法可能是有用的,尽管在失败情况下的性能几乎不相关,如果您只是旋转的话。pause或yield省电。在x86上,pause还提高了SMT的友好性,并且在另一个核心修改内存位置之后离开循环时避免了内存顺序错误的猜测。
但这就是为什么中国科学院有独立的memory_order参数来衡量成功和失败。松弛的失败可能会让编译器在离开循环路径时遇到唯一的障碍.
不过,atomic_flag test_and_set没有这个选择。手动操作可能会对AArch64这样的ISAs造成伤害,后者可能已经完成了RMW的获取,并避免了明确的隔离指令。(例如使用ldarb**)** )
哥德波特:使用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(是的,它看起来像是错过了一个优化,它只是用tst而不是cbnz w1, .L6来测试低的8位)
同时(放松的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没有dmb ishld,或者编译器不使用它。dmb ish,,,,,。
或使用-march=armv8.1-a
.L2:
swpab w20, w0, [x19]
tst w0, 255
bne .L2
mov x2, 19
...与
.L9:
swpb w20, w0, [x19]
tst w0, 255
bne .L9
dmb ishld # acquire barrier (load ordering)
mov x2, 19
...发布于 2020-06-11 11:13:44
暂停指令只是N个NOP指令的替换,其中N个处理器不同。此外,它对无序执行能力处理器中指令的重新排序有影响.atomic_thread_fence是否会比“暂停”提供一些好处取决于典型的循环自旋等待次数。atomic_thread_fence比暂停指令具有更高的执行延迟。如果旋转等待周期大于其他机制,如在x86平台上使用监控器-MWAIT指令对,则具有更好的性能和能源效率。否则暂停就足够了。
https://stackoverflow.com/questions/62318642
复制相似问题