簡體   English   中英

標准::原子<bool> ARM 上的無鎖不一致(樹莓派 3)

[英]std::atomic<bool> lock-free inconsistency on ARM (raspberry pi 3)

我有一個靜態斷言的問題。 靜態斷言完全是這樣的:

static_assert(std::atomic<bool>::is_always_lock_free);

並且代碼在 Raspberry Pi 3 上失敗(Linux raspberrypi 4.19.118-v7+ #1311 SMP Mon Apr 27 14:21:24 BST 2020 armv7l GNU/Linux)。

cppreference.com atomic::is_always_lock_free 參考站點上指出:

如果此原子類型始終是無鎖的,則等於 true,如果它從不或有時是無鎖的,則等於 false。 該常量的值與宏 ATOMIC_xxx_LOCK_FREE 的定義、成員函數 is_lock_free 和非成員函數 std::atomic_is_lock_free 一致。

對我來說,第一個奇怪的事情是“有時無鎖”。 它取決於什么? 不過后面的問題,回到問題上來。

我做了一個小測試。 寫了這段代碼:

#include <iostream>
#include <atomic>

int main()
{
    std::atomic<bool> dummy {};
    std::cout << std::boolalpha
            << "ATOMIC_BOOL_LOCK_FREE --> " << ATOMIC_BOOL_LOCK_FREE << std::endl
            << "dummy.is_lock_free() --> " << dummy.is_lock_free() << std::endl
            << "std::atomic_is_lock_free(&dummy) --> " << std::atomic_is_lock_free(&dummy) << std::endl
            << "std::atomic<bool>::is_always_lock_free --> " << std::atomic<bool>::is_always_lock_free << std::endl;
    return 0;
}

使用g++ -std=c++17 atomic_test.cpp && ./a.out 7.3.0 和 8.3.0,但這應該無關緊要)在樹莓上編譯並運行它並得到:

ATOMIC_BOOL_LOCK_FREE --> 1
dummy.is_lock_free() --> true
std::atomic_is_lock_free(&dummy) --> true
std::atomic<bool>::is_always_lock_free --> false

正如您所看到的,它不像 cppreference 站點上所說的那樣一致......為了進行比較,我在我的筆記本電腦(Ubuntu 18.04.5)上使用 g++ 7.5.0 運行它並得到:

ATOMIC_BOOL_LOCK_FREE --> 2
dummy.is_lock_free() --> true
std::atomic_is_lock_free(&dummy) --> true
std::atomic<bool>::is_always_lock_free --> true

所以ATOMIC_BOOL_LOCK_FREE的值和is_always_lock_free常量是有區別的。 尋找ATOMIC_BOOL_LOCK_FREE的定義,我能找到的是

c++/8/bits/atomic_lockfree_defines.h: #define ATOMIC_BOOL_LOCK_FREE  __GCC_ATOMIC_BOOL_LOCK_FREE
c++/8/atomic: static constexpr bool is_always_lock_free = ATOMIC_BOOL_LOCK_FREE == 2;

ATOMIC_BOOL_LOCK_FREE (或__GCC_ATOMIC_BOOL_LOCK_FREE )等於 1 或 2 有什么區別? 如果 1 那么它可能是或可能不是無鎖的,如果 2 是 100% 無鎖的,是否是這樣? 除了0還有其他值嗎? 這是 cppreference 站點上聲明所有這些返回值應該一致的錯誤嗎? 樹莓派輸出的哪些結果是真的?

ATOMIC_xxx_LOCK_FREE宏表示:

  • 0​為內置的原子類型,可從來沒有無鎖
  • 1用於有時無鎖的內置原子類型
  • 2對於始終無鎖的內置原子類型。

因此,在您的 PI 環境中, std::atomic<bool>有時是無鎖的,而您正在測試的dummy實例是無鎖的 -​​ 這意味着所有實例都是。

bool std::atomic_is_lock_free( const std::atomic<T>* obj ) :

在任何給定的程序執行中,無鎖查詢的結果對於相同類型的所有指針都是相同的。

唯一的缺點是,在運行程序之前,您不知道該類型是否是無鎖的。

If(not std::atomic_is_lock_free(&dummy)) {
    std::cout << "Sorry, the program will be slower than expected\n";
}

1在標准中表示“有時無鎖”。 但實際上這意味着“在編譯時不知道是無鎖的”。

如果沒有編譯器選項,GCC 的默認基線包括很舊的 ARM 芯片,以至於它們不支持原子 RMW 的必要指令,因此它必須編寫可以在古老的 CPU 上運行的代碼,總是調用 libatomic 函數而不是內聯原子操作。

當您在帶有 ARMv7 或 ARMv8 CPU 的 RPi 上運行運行時查詢函數時,它會返回 true。

使用-march=native-mcpu=cortex-a53你會得到is_always_lock_free是真的,因為在編譯時就知道目標機器肯定支持所需的指令。 (這些選項告訴 GCC 制作一個可能無法在其他/較舊 CPU 上運行的二進制文件。) OP 在評論中證實了這一點。

如果沒有那個編譯選項, std::atomic操作必須調用 libatomic 函數,所以即使在現代 CPU 上也會有額外的開銷。

GCC(和所有健全的編譯器)實現std::atomic<T> ,它要么對所有實例無鎖,要么沒有,不檢查對齊或每個對象在運行時的任何內容。

alignof( std::atomic<int64_t> )是 8,即使alignof( int64_t )在 32 位機器上只有 4,所以如果你有一個未對齊的原子對象,它是未定義的行為。 (對於純加載和純存儲,UB 的實際症狀可能包括撕裂,即非原子性。)如果您遵循 C++ 規則,您的所有原子對象都將對齊; 如果您將未對齊的指針投射到atomic<int64_t> *並嘗試使用它,您只會遇到問題。

暫無
暫無

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

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