如何实现atomic_flag?

问题描述 投票:0回答:2

atomic_flag如何实现?在我看来,在x86-64上它无论如何都等于atomic_bool,但这只是一个猜测。 x86-64实现与arm或x86有什么不同吗?

c++ arm x86-64 atomic stdatomic
2个回答
0
投票

是的,在atomic<bool>atomic<int>也是无锁的普通CPU上,使用相同的指令非常类似于atomic<bool>。 (x86和x86-64具有相同的原子操作集。)

[您可能会认为它总是使用x86 lock btslock btr来设置/重置(清除)单个位,但是做其他事情会更有效(特别是对于返回布尔值而不是布尔值的函数)分支)。对象是整个字节,因此您可以存储或交换整个字节。 (并且,如果ABI保证该值始终为01,则在将结果作为bool返回之前,不必对它进行布尔化处理))>

GCC和clang将test_and_set编译为字节交换,并清除为0的字节存储。

对于atomic_flag test_and_set,我们得到(几乎)相同的asm为f.exchange(true);
#include <atomic>

bool TAS(std::atomic_flag &f) {
    return f.test_and_set();
}

bool TAS_bool(std::atomic<bool> &f) {
    return f.exchange(true);
}


void clear(std::atomic_flag &f) {
    //f = 0; // deleted
    f.clear();
}

void clear_relaxed(std::atomic_flag &f) {
    f.clear(std::memory_order_relaxed);
}

void bool_clear(std::atomic<bool> &f) {
    f = false; // deleted
}

[带有CCC和clang的x86-64,以及ARMv7和AArch64的On Godbolt

## GCC9.2 -O3 for x86-64
TAS(std::atomic_flag&):
        mov     eax, 1
        xchg    al, BYTE PTR [rdi]
        ret
TAS_bool(std::atomic<bool>&):
        mov     eax, 1
        xchg    al, BYTE PTR [rdi]
        test    al, al
        setne   al                      # missed optimization, doesn't need to booleanize to 0/1
        ret
clear(std::atomic_flag&):
        mov     BYTE PTR [rdi], 0
        mfence                          # memory fence to drain store buffer before future loads
        ret
clear_relaxed(std::atomic_flag&):
        mov     BYTE PTR [rdi], 0      # x86 stores are already mo_release, no barrier
        ret
bool_clear(std::atomic<bool>&):
        mov     BYTE PTR [rdi], 0
        mfence
        ret

注意xchg也是在x86-64上进行seq_cst存储的有效方法,通常比gcc使用的mov + mfence更有效。 Clang对所有这些都使用xchg(休闲商店除外)。

有趣的是,在atomic_flag.test_and_set()中的x​​chg之后,clang将布尔值重新布尔化为0/1,但是GCC在atomic<bool>之后将其重新布尔化为0/1。 clang在TAS_bool中执行了一个奇怪的and al,1,会将2之类的值视为false。似乎毫无意义。 ABI保证内存中的bool始终存储为01字节。

对于ARM,我们有ldrexb / strexb交换重试循环,或者对于纯存储只有strb + dmb ish。或者,AArch64可以将stlrb wzr, [x0]用作clear或使用assign-false来进行(零寄存器的)顺序释放存储,而无需设置障碍。


0
投票

在大多数/健全的体系结构上,中断可能在执行硬件指令之后或之前发生。不在执行过程中。因此,要么指令“发生”(即带有“副作用”),要么不会发生。

© www.soinside.com 2019 - 2024. All rights reserved.