13

在 iOS 上,有两个类似的功能,OSAtomicAdd32OSAtomicAdd32Barrier. 我想知道你什么时候需要这个Barrier变体。

拆开后分别是:

_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指令可以重新排序吗?如果可以,原子更新是否有可能在没有障碍的情况下“丢失”?从我读过的内容来看,这似乎不会发生,如果是真的,那你为什么需要这个Barrier变体?也许只有当你也碰巧需要一个DMB用于其他目的的时候?

谢谢!

4

3 回答 3

20

哦,弱记忆排序的令人费解的恐怖......

第一个片段是您的基本原子读取-修改-写入-如果其他人触摸x1了指向的任何地址,则 store-exclusive 将失败,它将再次尝试直到成功。到目前为止,一切都很好。但是,这仅适用于独占监视器所覆盖的地址(或更正确的区域),因此虽然它有利于原子性,但对于除该值之外的任何内容的同步都是无效的。

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

  1. CPU0 写入数据地址。
  2. CPU0 递增信号量(原子地,就像你做的那样),它恰好在内存中的其他地方。
  3. ???
  4. CPU1 看到新的信号量值。
  5. CPU1 读取一些数据,这些数据可能是也可能不是旧数据、新数据或两者的某种混合。

现在,第 3 步发生了什么?也许这一切都是按顺序发生的。很可能,硬件决定,由于没有地址依赖性,它将让对信号量的存储先于对数据地址的存储。也许信号量存储在缓存中命中,而数据没有。也许只是因为只有那些硬件人员才能理解的复杂原因才这样做。无论哪种方式,CPU1 都完全有可能在新数据到达内存之前看到信号量更新,从而读回无效数据。

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

现在,更不直观的部分是 CPU1 还可以重新排序其负载。同样,由于不存在地址依赖性,因此无论 CPU0 的屏障如何,都可以在信号量加载之前随意推测数据加载。因此 CPU1 在第 4 步和第 5 步之间也需要自己的屏障。

对于更权威但相当繁重的版本,请阅读 ARM 的Barrier Litmus Tests and Cookbook。请注意,这些东西可能会令人困惑;)

顺便说一句,在这种情况下,获取/释放的架构语义使事情变得更加复杂。由于它们只是单向障碍,虽然OSAtomicAdd32Barrier相对于它之前和之后的代码加起来是一个完整的障碍,但它实际上并不能保证与原子操作本身相关的任何顺序 -有关更多解释,请参见Linux 的讨论。当然,这是从架构的理论角度来看的;实际上,A7 硬件采用了“简单”选项,即连接LDAXR到 just doDMB+LDXR等等,这并不是不可想象的,这意味着他们可以摆脱这一点,因为他们可以自由地编写自己的实现代码,而不是规格。

于 2014-02-03T21:37:03.250 回答
8

OSAtomicAdd32Barrier()存在于那些正在使用OSAtomicAdd()超出原子增量的东西的人。具体来说,他们正在实现自己的基于OSAtomicAdd(). 例如,创建自己的互斥库。 OSAtomicAdd32Barrier()使用重屏障指令在原子操作的两侧强制执行内存排序。这在正常使用中是不可取的。

总结一下:

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

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

于 2014-02-04T01:43:39.777 回答
3

我猜这只是为这个操作复制现有的独立于架构的语义的一种方式。

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

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

它比您在普通本机同步机制中使用的ldxar/stxrldxr/效率低stlxr,但如果您有现有的平台无关代码期望具有这些语义的原子添加,这可能是实现它的最佳方式。

于 2014-02-03T21:35:11.853 回答