簡體   English   中英

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

[英]Partial reordering of C++11 atomics on Aarch64

我在查看gcc的rmw原子編譯器輸出時 ,發現有些奇怪的東西-在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之后發生的裝入和存儲/存儲(請參見此處 )。 GCC不會添加圍欄來防止這種情況-以下是一小段代碼演示了這一點:

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操作前后的兩個操作重新排序。 如果邊界操作各自重新排序到操作的中間,然后彼此越過,則可能會發生這種情況。

看起來你是對的。 至少,已經接受並修復了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 confirmance。 aarch64上的障礙的文檔清楚地說明了這種重新排序是可能的。

我在這里沒有發現任何問題。 第二個示例與第一個示例幾乎相同,只是在其中預先添加了一個負載並附加了一個負載。 因此,它完全遵循您在C代碼中編寫的內容。 我在這里看不到任何重新排序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM