Aarch64上C ++ 11原子的部分重新排序

时间:2016-02-05 06:11:24

标签: c++ multithreading c++11 concurrency atomic

我正在查看compiler output of rmw atomics from gcc并注意到一些奇怪的事情 - 在Aarch64上,诸如fetch_add之类的rmw操作可以通过宽松的加载进行部分重新排序。

在Aarch64上,可能会为value.fetch_add(1, seq_cst)

生成以下代码
.L1:
    ldaxr x1, [x0]
    add x1, x1, 1
    stlxr w2, x1, [x0]
    cbnz L1

然而,在ldaxr之前发生的加载和存储可能会重新排序经过stlxr之后发生的加载和加载/存储(参见here)。海湾合作委员会没有添加围栏以防止这种情况 - 这里有一小段代码证明了这一点:

void partial_reorder(std::atomic<uint64_t> loader, std::atomic<uint64_t> adder) {
    loader.load(std::memory_order_relaxed); // can be reordered past the ldaxr
    adder.fetch_add(1, std::memory_order_seq_cst);
    loader.load(std::memory_order_relaxed); // can be reordered past the stlxr
}

生成

partial_reorder(std::atomic<int>, std::atomic<int>):
    ldr     w2, [x0] @ reordered down
.L2:
    ldaxr   w2, [x1]
    add     w2, w2, 1
    stlxr   w3, w2, [x1]
    cbnz    w3, .L2
    ldr     w0, [x0] @ reordered up
    ret

实际上,可以使用RMW操作对负载进行部分重新排序 - 它们位于中间。

那么,最重要的是什么?我在问什么?

  1. 原子操作可以被这样整除,这似乎很奇怪。我无法在标准中发现任何阻止这种情况的内容,但我相信有一系列规则暗示操作是不可分割的。

  2. 看起来这并不尊重获取订购。如果我在此操作之后直接执行加载,我可以在fetch_add和后面的操作之间看到存储加载或存储存储重新排序,这意味着稍后的内存访问至少部分地在获取操作后重新排序。同样,我无法在标准中明确地发现任何不允许和获取的内容是加载顺序,但我的理解是,获取操作适用于整个操作而不仅仅是部分操作。类似的情况可以适用于释放某些内容经过ldaxr重新排序的内容。

  3. 这个可能会更多地延伸排序定义,但是seq_cst操作之前和之后的两个操作可以重新排序,看起来无效。如果边界操作每个都重新排序到操作的中间,然后相互经过,则可能发生(?)。

2 个答案:

答案 0 :(得分:4)

看起来你是对的。至少,非常相似bug for gcc已被接受并修复。

他们提供此代码:

.L2:
    ldaxr   w1, [x0]       ; load-acquire (__sync_fetch_and_add)
    add w1, w1, 1
    stlxr   w2, w1, [x0]   ; store-release  (__sync_fetch_and_add)
    cbnz    w2, .L2

因此可以使用ldaxr对以前的操作进行重新排序,并且可以使用stlxr对其进行重新排序,从而打破C ++ 11验证。 Documentation对于aarch64的障碍清楚地解释说,这样的重新排序是可能的。

答案 1 :(得分:0)

我在这里看不出任何错误。第二个示例与第一个部分几乎相同,其中一个负载前置并附加了负载。所以它完全遵循你在C代码中写的内容。我在这里看不到任何重新排序。