Mengapa std :: atomic <T> :: is_lock_free () tidak statis dan juga constexpr?

9

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.

Bonita Montero
sumber
3
Apakah kamu sadar is_always_lock_free?
Mike van Dyke
3
Saya akan membuang "alignment" di luar sana.
Max Langhof
@ MaxLanghof Maksud Anda tidak semua instance akan disejajarkan dengan cara yang sama?
curiousguy
1
Mike, tidak, saya tidak sadar, tapi terima kasih untuk petunjuk ini; Ini sangat menolong bagi saya. Tapi saya bertanya pada diri sendiri mengapa ada keputusan antara is_lock_free () dan is_always_lock_free. Itu tidak mungkin karena atom yang tidak selaras, kata orang lain yang disarankan di sini, karena bahasa mendefinisikan akses yang tidak selaras memiliki perilaku yang tidak terdefinisi.
Bonita Montero

Jawaban:

10

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_freemungkin 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) == 8untuk 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!

Max Langhof
sumber
1
32-bit x86 adalah contoh yang baik dari mana Anda menemukan ABI alignof(double)==4. Tetapi std::atomic<double>masih memiliki alignof() = 8bukannya 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!)
Peter Cordes
2
Tetapi implementasi yang benar dari C ++ 20 std::atomic_ref<double>akan menolak doublesepenuhnya tidak selaras , atau akan memeriksa keselarasan pada saat runtime pada platform di mana itu legal untuk polos doubledan int64_tmenjadi 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.)
Peter Cordes
2
Lihat gcc.gnu.org/bugzilla/show_bug.cgi?id=62259 untuk bug libstdc ++ yang sekarang diperbaiki, dan gcc.gnu.org/bugzilla/show_bug.cgi?id=65146 untuk bug C yang masih rusak, termasuk testcase ISO C11 murni yang menunjukkan sobek _Atomic int64_tsaat 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 packatau __attribute__((packed))hanya akan mengarah pada non-atomisitas; objek masih akan melaporkan bahwa mereka adalah lock_free.
Peter Cordes
1
Tapi ya, tujuannya 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.
Peter Cordes
3

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.

sayang
sumber
1
std::numeric_limits<int>::maxtergantung pada arsitektur, namun statis dan constexpr. Saya kira tidak ada yang salah dalam jawabannya, tetapi saya tidak membeli bagian pertama dari alasan
idclev 463035818
1
Tidak mendefinisikan bahasa akses yang tidak selaras memiliki perilaku yang tidak terdefinisi sehingga evaluasi penguncian-bebas atau tidak pada saat runtime akan menjadi omong kosong?
Bonita Montero
1
Tidak masuk akal untuk memutuskan antara akses yang selaras dan tidak selaras karena bahasa mendefinisikan yang terakhir sebagai perilaku yang tidak terdefinisi.
Bonita Montero
@BonitaMontero Ada arti "tidak selaras dalam C ++ objek alignment" dan "tidak selaras dalam apa yang disukai perangkat keras" pengertian. Itu tidak selalu sama, tetapi dalam praktiknya sering kali sama. Contoh Anda menunjukkan adalah salah satu contoh seperti mana kompilator tampaknya memiliki built-in asumsi bahwa dua adalah sama - yang hanya berarti bahwa is_lock_freeada gunanya pada compiler yang .
Max Langhof
1
Anda bisa sangat yakin bahwa atom akan memiliki keselarasan yang tepat jika ada persyaratan keselarasan.
Bonita Montero
1

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 ldpatau 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 truealias 1.

Implementasi ini memilih untuk digunakan alignof( atomic<int64_t> ) == 8sehingga 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_freesangat berguna: karena biasanya benar untuk jenis is_lock_free()yang pernah benar.)

Bonita Montero
sumber
1
Ya, sebagian besar implementasi memilih untuk memberi atomic<uint64_t>dan alignof() == 8karenanya 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_tmungkin hanya memiliki keselarasan 4-byte, atomic<int64_t>membutuhkan 8-byte. Lihat komentar saya pada jawaban lain
Peter Cordes
Dimasukkan ke dalam kata-kata yang berbeda: Jika seorang kompiler memilih untuk membuat alignofnilai 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.
Max Langhof
1
Anda dapat yakin bahwa jika bahasa tersebut mengatakan bahwa akses yang tidak selaras memiliki perilaku yang tidak terdefinisi, semua atom harus disejajarkan dengan benar. Tidak ada implementasi yang akan melakukan pemeriksaan runtime karena itu.
Bonita Montero
@BonitaMontero Ya, tapi tidak ada dalam bahasa yang melarang 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 doublepada 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).
Max Langhof