Qualcuno può dirmi se std :: atomic :: is_lock_free () non è statico e constexpr? Avere questo non statico e / o come non-constexpr non ha senso per me.
Qualcuno può dirmi se std :: atomic :: is_lock_free () non è statico e constexpr? Avere questo non statico e / o come non-constexpr non ha senso per me.
Risposte:
Come spiegato su cppreference :
Tutti i tipi atomici ad eccezione di std :: atomic_flag possono essere implementati utilizzando mutex o altre operazioni di blocco, anziché utilizzare le istruzioni della CPU atomica senza blocco. A volte i tipi atomici possono anche essere liberi da blocchi, ad esempio se solo gli accessi di memoria allineati sono naturalmente atomici su una data architettura, gli oggetti disallineati dello stesso tipo devono usare i blocchi.
Lo standard C ++ raccomanda (ma non richiede) che anche le operazioni atomiche senza blocco siano prive di indirizzi, cioè adatte alla comunicazione tra processi che utilizzano la memoria condivisa.
Come menzionato da molti altri, std::is_always_lock_free
potrebbe essere quello che stai davvero cercando.
Modifica: per chiarire, i tipi di oggetti C ++ hanno un valore di allineamento che limita gli indirizzi delle loro istanze a solo alcuni multipli di potenze di due ( [basic.align]
). Questi valori di allineamento sono definiti dall'implementazione per i tipi fondamentali e non devono necessariamente essere uguali alle dimensioni del tipo. Possono anche essere più severi di quanto l'hardware possa effettivamente supportare.
Ad esempio, x86 (principalmente) supporta accessi non allineati. Tuttavia, troverai la maggior parte dei compilatori che hanno alignof(double) == sizeof(double) == 8
x86, poiché gli accessi non allineati presentano una serie di svantaggi (velocità, memorizzazione nella cache, atomicità ...). Ma ad esempio #pragma pack(1) struct X { char a; double b; };
o alignas(1) double x;
ti permette di avere "non allineati" double
. Quindi, quando cppreference parla di "accessi di memoria allineati", presumibilmente lo fa in termini di naturale allineamento del tipo per l'hardware, non usando un tipo C ++ in un modo che contraddice i suoi requisiti di allineamento (che sarebbe UB).
Ecco maggiori informazioni: Qual è l'effetto effettivo di accessi non allineati riusciti su x86?
Leggi anche i commenti perspicaci di @Peter Cordes qui sotto!
alignof(double)==4
. Ma std::atomic<double>
ha ancora alignof() = 8
invece di verificare l'allineamento in fase di esecuzione. L'uso di una struttura impacchettata che disallinea gli atomici interrompe l'ABI e non è supportato. (GCC per x86 a 32 bit preferisce fornire un allineamento naturale degli oggetti a 8 byte, ma le regole di impacchettamento hanno la precedenza su quelle e si basano solo alignof(T)
, ad esempio su i386 System V. G ++ usato per avere un bug in cui atomic<int64_t>
all'interno di una struttura potrebbe non essere atomico perché ha appena assunto. GCC (per C non C ++) ha ancora questo bug!)
std::atomic_ref<double>
rifiuterà completamente il sotto-allineamento double
o verificherà l'allineamento in fase di esecuzione su piattaforme in cui è legale per il semplice double
e int64_t
per essere meno di quanto naturalmente allineato. (Perché atomic_ref<T>
opera su un oggetto che è stato dichiarato come pianura T
e ha solo un allineamento minimo alignof(T)
senza la possibilità di dargli un allineamento extra.)
_Atomic int64_t
quando compilato con la corrente gcc -m32
. Ad ogni modo, il mio punto è che i compilatori reali non supportano l'atomica sotto allineata e non eseguono controlli di runtime (ancora?), Quindi #pragma pack
o __attribute__((packed))
porteranno semplicemente alla non atomicità; gli oggetti segnaleranno comunque che lo sono lock_free
.
is_lock_free()
è quello di consentire alle implementazioni di funzionare in modo diverso rispetto a quelle attuali; con controlli di runtime basati sull'allineamento effettivo per utilizzare istruzioni atomiche supportate da HW o per utilizzare un blocco.
Puoi usare std::is_always_lock_free
is_lock_free
dipende dal sistema reale e non può essere determinato al momento della compilazione.
Spiegazione pertinente:
A volte i tipi atomici possono anche essere liberi da blocchi, ad esempio se solo gli accessi di memoria allineati sono naturalmente atomici su una data architettura, gli oggetti disallineati dello stesso tipo devono usare i blocchi.
std::numeric_limits<int>::max
dipende dall'architettura, eppure è statico e constexpr
. Immagino che non ci sia nulla di sbagliato nella risposta, ma non compro la prima parte del ragionamento
is_lock_free
senso su quel compilatore .
Ho installato Visual Studio 2019 sul mio PC Windows e questo devenv ha anche un compilatore ARMv8. ARMv8 consente accessi non allineati, ma il confronto e gli scambi, le aggiunte bloccate ecc. Sono obbligati ad essere allineati. E anche il carico puro / archivio puro che utilizza ldp
o stp
(coppia di carico o coppia di magazzini di registri a 32 bit) è garantito per essere atomico solo quando sono naturalmente allineati.
Quindi ho scritto un piccolo programma per verificare cosa ritorna is_lock_free () per un puntatore atomico arbitrario. Quindi ecco il codice:
#include <atomic>
#include <cstddef>
using namespace std;
bool isLockFreeAtomic( atomic<uint64_t> *a64 )
{
return a64->is_lock_free();
}
E questo è lo smontaggio di isLockFreeAtomic
|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC
movs r0,#1
bx lr
ENDP
Questo è solo returns true
, aka 1
.
Questa implementazione sceglie di usare in alignof( atomic<int64_t> ) == 8
modo che ogni atomic<int64_t>
sia allineato correttamente. Ciò evita la necessità di controlli di allineamento di runtime su ogni carico e archivio.
(Nota del redattore: questo è comune; la maggior parte delle implementazioni C ++ nella vita reale funzionano in questo modo. Ecco perché std::is_always_lock_free
è così utile: perché di solito è vero per i tipi dove is_lock_free()
è sempre vero.)
atomic<uint64_t>
e alignof() == 8
quindi non è necessario verificare l'allineamento in fase di esecuzione. Questa vecchia API offre loro la possibilità di non farlo, ma sull'attuale HW ha molto più senso solo richiedere l'allineamento (altrimenti UB, ad es. Non atomicità). Anche nel codice a 32 bit in cui int64_t
potrebbe essere presente solo un allineamento a 4 byte, atomic<int64_t>
richiede 8 byte. Vedi i miei commenti su un'altra risposta
alignof
valore per un tipo fondamentale uguale al "buon" allineamento dell'hardware, allora is_lock_free
sarà sempre true
(e così sarà is_always_lock_free
). Il tuo compilatore qui fa esattamente questo. Ma l'API esiste, quindi altri compilatori possono fare cose diverse.
alignof(std::atomic<double>) == 1
(quindi non ci sarebbe un "accesso non allineato" in senso C ++, quindi nessun UB), anche se l'hardware può garantire operazioni atomiche senza blocco per double
s su 4 o Confini di 8 byte. Il compilatore dovrebbe quindi utilizzare i blocchi nei casi non allineati (e restituire il valore booleano appropriato da is_lock_free
, a seconda della posizione di memoria dell'istanza dell'oggetto).
is_always_lock_free
?