Adakah yang bisa memberi tahu saya apakah std :: atomic :: is_lock_free () tidak statis dan juga constexpr? Setelah itu non-statis dan / atau sebagai non-constexpr tidak masuk akal bagi saya.
Adakah yang bisa memberi tahu saya apakah std :: atomic :: is_lock_free () tidak statis dan juga constexpr? Setelah itu non-statis dan / atau sebagai non-constexpr tidak masuk akal bagi saya.
Jawaban:
Seperti yang dijelaskan pada cppreference :
Semua tipe atom kecuali untuk std :: atomic_flag dapat diimplementasikan menggunakan mutex atau operasi penguncian lainnya, daripada menggunakan instruksi atom CPU bebas kunci. Jenis atom juga diperbolehkan bebas dari kunci, misalnya jika hanya akses memori yang disejajarkan secara alami atom pada arsitektur yang diberikan, objek yang tidak selaras dengan jenis yang sama harus menggunakan kunci.
Standar C ++ merekomendasikan (tetapi tidak mengharuskan) bahwa operasi atom bebas kunci juga bebas alamat, yaitu, cocok untuk komunikasi antara proses menggunakan memori bersama.
Seperti yang disebutkan oleh banyak orang lain, std::is_always_lock_free
mungkin apa yang sebenarnya Anda cari.
Sunting: Untuk memperjelas, tipe objek C ++ memiliki nilai penyelarasan yang membatasi alamat instansnya hanya beberapa kelipatan kekuatan dua ( [basic.align]
). Nilai-nilai penyelarasan ini didefinisikan untuk tipe dasar, dan tidak perlu sama dengan ukuran tipe. Mereka juga bisa lebih ketat daripada apa yang sebenarnya bisa didukung oleh perangkat keras.
Misalnya, x86 (sebagian besar) mendukung akses yang tidak selaras. Namun, Anda akan menemukan kebanyakan kompiler memiliki alignof(double) == sizeof(double) == 8
untuk x86, karena akses yang tidak selaras memiliki sejumlah kelemahan (kecepatan, caching, atomicity ...). Tapi misalnya #pragma pack(1) struct X { char a; double b; };
atau alignas(1) double x;
memungkinkan Anda untuk memiliki "tidak selaras" double
. Jadi ketika cppreference berbicara tentang "akses memori yang disejajarkan", itu mungkin dilakukan dalam hal penyelarasan alami dari tipe untuk perangkat keras, tidak menggunakan tipe C ++ dengan cara yang bertentangan dengan persyaratan penyelarasannya (yang akan menjadi UB).
Berikut ini informasi lebih lanjut: Apa efek aktual dari akses yang tidak selaras pada x86?
Silakan juga periksa komentar mendalam dari @Peter Cordes di bawah ini!
alignof(double)==4
. Tetapi std::atomic<double>
masih memiliki alignof() = 8
bukannya memeriksa perataan saat runtime. Menggunakan struct dikemas yang di bawah-menyelaraskan atom istirahat ABI dan tidak didukung. (GCC untuk 32-bit x86 lebih suka memberikan keselarasan objek alami 8-byte, tetapi aturan struct-packing menimpanya dan hanya didasarkan pada alignof(T)
, mis. Pada Sistem i386 V. G ++ dulu memiliki bug di mana atomic<int64_t>
di dalam struct mungkin bukan atom. karena hanya diasumsikan. GCC (untuk C bukan C ++) masih memiliki bug ini!)
std::atomic_ref<double>
akan menolak double
sepenuhnya tidak selaras , atau akan memeriksa keselarasan pada saat runtime pada platform di mana itu legal untuk polos double
dan int64_t
menjadi kurang dari rata alami. (Karena atomic_ref<T>
beroperasi pada objek yang dinyatakan sebagai dataran T
, dan hanya memiliki penyelarasan minimum alignof(T)
tanpa kesempatan untuk memberikan penyelarasan tambahan.)
_Atomic int64_t
saat dikompilasi dengan arus gcc -m32
. Pokoknya, maksud saya adalah bahwa kompiler nyata tidak mendukung atom yang tidak selaras, dan tidak melakukan pemeriksaan runtime (belum?), Jadi #pragma pack
atau __attribute__((packed))
hanya akan mengarah pada non-atomisitas; objek masih akan melaporkan bahwa mereka adalah lock_free
.
is_lock_free()
adalah untuk memungkinkan implementasi untuk bekerja secara berbeda dari cara yang sebenarnya dilakukan; dengan pemeriksaan runtime berdasarkan penyelarasan aktual untuk menggunakan instruksi atom yang didukung HW atau menggunakan kunci.
Anda bisa menggunakan std::is_always_lock_free
is_lock_free
tergantung pada sistem aktual dan tidak dapat ditentukan pada waktu kompilasi.
Penjelasan yang relevan:
Jenis atom juga diperbolehkan bebas dari kunci, misalnya, jika hanya akses memori yang disejajarkan secara alami adalah atom pada arsitektur yang diberikan, objek yang tidak selaras dengan jenis yang sama harus menggunakan kunci.
std::numeric_limits<int>::max
tergantung pada arsitektur, namun statis dan constexpr
. Saya kira tidak ada yang salah dalam jawabannya, tetapi saya tidak membeli bagian pertama dari alasan
is_lock_free
ada gunanya pada compiler yang .
Saya telah menginstal Visual Studio 2019 pada Windows-PC saya dan devenv ini juga memiliki kompiler ARMv8. ARMv8 memungkinkan akses yang tidak selaras, tetapi bandingkan dan tukar, tambah terkunci, dll. Diamanatkan untuk disejajarkan. Dan juga murni load / penyimpanan murni menggunakan ldp
atau stp
(load-pair atau store-pair dari register 32-bit) hanya dijamin atom ketika mereka secara alami selaras.
Jadi saya menulis sebuah program kecil untuk memeriksa apa yang dikembalikan is_lock_free () untuk penunjuk atom-arbitrary. Jadi, inilah kodenya:
#include <atomic>
#include <cstddef>
using namespace std;
bool isLockFreeAtomic( atomic<uint64_t> *a64 )
{
return a64->is_lock_free();
}
Dan ini adalah pembongkaran isLockFreeAtomic
|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC
movs r0,#1
bx lr
ENDP
Ini hanya returns true
alias 1
.
Implementasi ini memilih untuk digunakan alignof( atomic<int64_t> ) == 8
sehingga setiap atomic<int64_t>
selaras. Ini menghindari perlunya pemeriksaan keselarasan runtime pada setiap beban dan penyimpanan.
(Catatan editor: ini biasa; implementasi C ++ paling nyata bekerja dengan cara ini. Inilah mengapa std::is_always_lock_free
sangat berguna: karena biasanya benar untuk jenis is_lock_free()
yang pernah benar.)
atomic<uint64_t>
dan alignof() == 8
karenanya mereka tidak perlu memeriksa perataan saat runtime. API lama ini memberi mereka pilihan untuk tidak melakukannya, tetapi pada HW saat ini, jauh lebih masuk akal untuk meminta penyelarasan (jika tidak, UB, mis. Non-atomisitas). Bahkan dalam kode 32-bit di mana int64_t
mungkin hanya memiliki keselarasan 4-byte, atomic<int64_t>
membutuhkan 8-byte. Lihat komentar saya pada jawaban lain
alignof
nilai untuk tipe dasar sama dengan penyelarasan "baik" dari perangkat keras, maka is_lock_free
akan selalu true
(dan begitu juga is_always_lock_free
). Kompiler Anda di sini melakukan hal ini. Tetapi API ada sehingga kompiler lain dapat melakukan hal yang berbeda.
alignof(std::atomic<double>) == 1
(jadi tidak akan ada "akses tidak selaras" dalam arti C ++, maka tidak ada UB), bahkan jika perangkat keras hanya dapat menjamin operasi atom bebas kunci untuk double
pada 4 atau Batas 8 byte. Compiler kemudian harus menggunakan kunci dalam kasus yang tidak selaras (dan mengembalikan nilai boolean yang sesuai is_lock_free
, tergantung pada lokasi memori dari instance objek).
is_always_lock_free
?