ARM64:LDXR/STXR与LDAXR/STLXR

ARM64:LDXR/STXR与LDAXR/STLXR,arm,atomic,arm64,Arm,Atomic,Arm64,在iOS上,有两个类似的函数,OSAtomicAdd32和OSAtomicAdd32Barrier。我想知道您何时需要屏障变体 分解后,它们是: _OSAtomicAdd32: ldxr w8, [x1] add w8, w8, w0 stxr w9, w8, [x1] cbnz w9, _OSAtomicAdd32 mov x0, x8 ret lr _OSAtomicAdd32Barrier: ldaxr w8, [x1] add w8

在iOS上,有两个类似的函数,
OSAtomicAdd32
OSAtomicAdd32Barrier
。我想知道您何时需要
屏障
变体

分解后,它们是:

_OSAtomicAdd32:
ldxr    w8, [x1]
add     w8, w8, w0
stxr    w9, w8, [x1]
cbnz    w9, _OSAtomicAdd32
mov     x0, x8
ret     lr

_OSAtomicAdd32Barrier:
ldaxr   w8, [x1]
add     w8, w8, w0
stlxr   w9, w8, [x1]
cbnz    w9, _OSAtomicAdd32Barrier
mov     x0, x8
ret     lr
在哪些场景中,您需要后者的Load Acquire/Store Release语义?
LDXR
/
STXR
指令是否可以重新排序?如果可以,在没有屏障的情况下,原子更新是否可能“丢失”?据我所知,这似乎不可能发生,如果是真的,那么为什么需要
屏障
变体?也许只有当您碰巧出于其他目的需要一个
DMB


谢谢

我猜这只是一种为该操作复制现有架构无关语义的方法

使用
ldaxr
/
stlxr
对,如果将AtomicAdd32用作同步机制(互斥/信号量),则上述顺序将确保正确排序,而不管生成的更高级别操作是获取还是释放

所以-这不是关于强制原子添加的一致性,而是关于强制获取/释放互斥体和对该互斥体保护的资源执行的任何操作之间的顺序


它的效率低于在普通本机同步机制中使用的
ldxar
/
stxr
ldxr
/
stlxr
,但如果现有的平台无关代码需要使用这些语义进行原子添加,这可能是实现它的最佳方式。

,对弱记忆排序的扭曲心灵的恐惧

第一个代码段是基本的原子读-修改-写-如果其他人触摸了
x1
指向的任何地址,存储独占将失败,并将重试,直到成功。到现在为止,一直都还不错。但是,这只适用于独占监视器所覆盖的地址(或者更准确地说是区域),因此,虽然这有利于原子性,但对于除该值之外的任何其他值的同步都是无效的

假设CPU1正在等待CPU0将一些数据写入缓冲区。CPU1坐在那里等待某种类型的同步对象(比方说一个信号量),等待CPU0更新它以发出新数据准备就绪的信号

  • CPU0写入数据地址
  • CPU0增加恰好位于内存中其他位置的信号量(原子地,就像您所做的那样)
  • CPU1看到新的信号量值
  • CPU1读取一些数据,这些数据可能是或不是旧数据、新数据或两者的混合数据
  • 现在,第三步发生了什么?也许这一切都是按顺序发生的。很可能,硬件决定,由于没有地址依赖关系,它将让信号量的存储先于数据地址的存储。可能信号存储命中了缓存,而数据没有命中。也许这只是因为复杂的原因,只有那些硬件人员才明白。无论哪种方式,CPU1都完全有可能在新数据到达内存之前看到信号量更新,从而读回无效数据

    为了解决这个问题,CPU0必须在步骤1和步骤2之间设置一个屏障,以确保在写入信号量之前已经明确写入了数据。让原子写入成为一个屏障是一个很好的简单方法。但是,由于屏障会降低性能,因此在不需要这种完全同步的情况下,您也需要轻量级无屏障版本

    现在,更不直观的部分是CPU1还可以重新排序其负载。同样,由于没有地址依赖性,因此可以在信号量加载之前自由推测数据加载,而不考虑CPU0的屏障。因此,CPU1在步骤4和步骤5之间也需要它自己的屏障

    对于更权威,但相当沉重的版本,有一个ARM的阅读。注意,这些东西可能会让人困惑;)

    另一方面,在这种情况下,acquire/release的体系结构语义使事情更加复杂。由于它们只是单向屏障,而
    OSAtomicAdd32Barrier
    加起来是相对于前后代码的完整屏障,因此实际上并不保证相对于原子操作本身的任何排序-有关更多解释,请参阅。当然,这是从建筑学的理论观点来看的;事实上,A7硬件采用了“简单”的选项,即连接
    LDAXR
    来执行
    DMB+LDXR
    等等,这并非不可想象,这意味着他们可以不受惩罚,因为他们可以自由地为自己的实现而不是规范编写代码。

    OSAtomicAdd32Barrier()
    适用于使用
    OSAtomicAdd()
    进行原子增量以外的操作的人。具体来说,他们正在基于
    OSAtomicAdd()
    实现自己的多处理同步原语。例如,创建自己的互斥体库
    OSAtomicAdd32Barrier()
    使用重屏障指令在原子操作的两侧强制执行内存排序。这在正常使用中是不可取的

    总结如下:

    1) 如果您只想以线程安全的方式递增整数,请使用
    OSAtomicAdd32()


    2) 如果您被一堆愚蠢地认为
    OSAtomicAdd32()
    可以用作处理器间内存排序和推测屏障的旧代码所困扰,请将其替换为
    OSAtomicAdd32Barrier()

    ,感谢您编写的详细答案!我希望我能接受不止一个答案。在发布我的问题之前,我确实读了一点Barrier Litmus测试,但这些答案确实帮助我澄清了困惑。@DaveLee,老实说,这回答了问题:“原子更新是否可能在没有障碍的情况下‘丢失’?”而“已接受”的答案确实如此