Quelqu'un peut-il me dire si std :: atomic :: is_lock_free () n'est pas statique aussi bien que constexpr? L'avoir non statique et / ou non constexpr n'a pas de sens pour moi.
Quelqu'un peut-il me dire si std :: atomic :: is_lock_free () n'est pas statique aussi bien que constexpr? L'avoir non statique et / ou non constexpr n'a pas de sens pour moi.
Réponses:
Comme expliqué sur cppreference :
Tous les types atomiques à l'exception de std :: atomic_flag peuvent être implémentés en utilisant des mutex ou d'autres opérations de verrouillage, plutôt qu'en utilisant les instructions CPU atomiques sans verrouillage. Les types atomiques peuvent également être parfois sans verrouillage, par exemple si seuls les accès mémoire alignés sont naturellement atomiques sur une architecture donnée, les objets mal alignés du même type doivent utiliser des verrous.
La norme C ++ recommande (mais n'exige pas) que les opérations atomiques sans verrouillage soient également sans adresse, c'est-à-dire adaptées à la communication entre les processus utilisant la mémoire partagée.
Comme mentionné par plusieurs autres, c'est std::is_always_lock_free
peut-être ce que vous recherchez vraiment.
Edit: Pour clarifier, les types d'objets C ++ ont une valeur d'alignement qui restreint les adresses de leurs instances à seulement certains multiples de puissances de deux ( [basic.align]
). Ces valeurs d'alignement sont définies par l'implémentation pour les types fondamentaux et n'ont pas besoin d'être égales à la taille du type. Ils peuvent également être plus stricts que ce que le matériel pourrait réellement prendre en charge.
Par exemple, x86 (principalement) prend en charge les accès non alignés. Cependant, vous trouverez la plupart des compilateurs ayant alignof(double) == sizeof(double) == 8
pour x86, car les accès non alignés ont une foule d'inconvénients (vitesse, mise en cache, atomicité ...). Mais par exemple, #pragma pack(1) struct X { char a; double b; };
ou alignas(1) double x;
vous permet d'avoir des "non alignés" double
. Ainsi, lorsque cppreference parle d '"accès à la mémoire alignés", il le fait probablement en termes d'alignement naturel du type pour le matériel, n'utilisant pas un type C ++ d'une manière qui contredit ses exigences d'alignement (qui seraient UB).
Voici plus d'informations: Quel est l'effet réel des accès non alignés réussis sur x86?
Veuillez également consulter les commentaires perspicaces de @Peter Cordes ci-dessous!
alignof(double)==4
. Mais a std::atomic<double>
toujours alignof() = 8
au lieu de vérifier l'alignement lors de l'exécution. L'utilisation d'une structure compactée qui sous-aligne atomique rompt l'ABI et n'est pas prise en charge. (GCC pour x86 32 bits préfère donner un alignement naturel aux objets de 8 octets, mais les règles de struct-packing l'emportent et ne sont basées que sur alignof(T)
, par exemple, sur le système i386 V. G ++ avait un bogue où l' atomic<int64_t>
intérieur d'une structure peut ne pas être atomique car il vient de supposer. GCC (pour C pas C ++) a toujours ce bug!)
std::atomic_ref<double>
rejettera double
complètement sous-aligné ou vérifiera l'alignement au moment de l'exécution sur les plates-formes où il est légal pour plain double
et int64_t
d'être moins que naturellement aligné. (Parce qu'il atomic_ref<T>
opère sur un objet qui a été déclaré comme une plaine T
, et qui n'a qu'un alignement minimum de alignof(T)
sans la possibilité de lui donner un alignement supplémentaire.)
_Atomic int64_t
lorsqu'il est compilé avec le courant gcc -m32
. Quoi qu'il en soit, mon point est que les vrais compilateurs ne prennent pas en charge les atomiques sous-alignés, et ne font pas (encore?) De vérifications d'exécution, donc #pragma pack
ou __attribute__((packed))
mèneront simplement à la non-atomicité; les objets rapporteront toujours qu'ils le sont lock_free
.
is_lock_free()
est de permettre aux implémentations de fonctionner différemment de la manière actuelle; avec des contrôles d'exécution basés sur l'alignement réel pour utiliser des instructions atomiques prises en charge par HW ou pour utiliser un verrou.
Vous pouvez utiliser std::is_always_lock_free
is_lock_free
dépend du système réel et ne peut pas être déterminé au moment de la compilation.
Explication pertinente:
Les types atomiques peuvent également être parfois sans verrouillage, par exemple si seuls les accès mémoire alignés sont naturellement atomiques sur une architecture donnée, les objets mal alignés du même type doivent utiliser des verrous.
std::numeric_limits<int>::max
dépend de l'architecture, mais est statique et constexpr
. Je suppose qu'il n'y a rien de mal dans la réponse, mais je n'achète pas la première partie du raisonnement
is_lock_free
est inutile sur ce compilateur .
J'ai installé Visual Studio 2019 sur mon PC Windows et ce devenv a également un compilateur ARMv8. ARMv8 autorise les accès non alignés, mais la comparaison et les échanges, les ajouts verrouillés, etc. doivent être alignés. De plus, la charge pure / stockage pur utilisant ldp
ou stp
(paire de charge ou paire de magasins de registres 32 bits) ne sont garantis atomiques que lorsqu'ils sont naturellement alignés.
J'ai donc écrit un petit programme pour vérifier ce que is_lock_free () renvoie pour un pointeur atomique arbitraire. Voici donc le code:
#include <atomic>
#include <cstddef>
using namespace std;
bool isLockFreeAtomic( atomic<uint64_t> *a64 )
{
return a64->is_lock_free();
}
Et c'est le démontage de isLockFreeAtomic
|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC
movs r0,#1
bx lr
ENDP
C'est juste returns true
, aka 1
.
Cette implémentation choisit d'utiliser alignof( atomic<int64_t> ) == 8
afin que chaque atomic<int64_t>
soit correctement aligné. Cela évite d'avoir à effectuer des vérifications d'alignement d'exécution sur chaque chargement et chaque magasin.
(Note de l'éditeur: c'est courant; la plupart des implémentations C ++ réelles fonctionnent de cette façon. C'est pourquoi std::is_always_lock_free
c'est si utile: parce que c'est généralement vrai pour les types où is_lock_free()
c'est toujours vrai.)
atomic<uint64_t>
et alignof() == 8
n'ont donc pas à vérifier l'alignement au moment de l'exécution. Cette ancienne API leur donne la possibilité de ne pas le faire, mais sur le matériel actuel, il est beaucoup plus logique de simplement exiger l'alignement (sinon UB, par exemple la non-atomicité). Même dans le code 32 bits où l' int64_t
alignement sur 4 octets ne peut être requis atomic<int64_t>
que sur 8 octets. Voir mes commentaires sur une autre réponse
alignof
valeur d'un type fondamental la même chose que le "bon" alignement du matériel, il le is_lock_free
sera toujours true
(et il en sera de même is_always_lock_free
). Votre compilateur fait exactement cela. Mais l'API existe donc d'autres compilateurs peuvent faire des choses différentes.
alignof(std::atomic<double>) == 1
(il n'y aurait donc pas d '"accès non aligné" au sens C ++, donc pas d'UB), même si le matériel ne peut garantir que des opérations atomiques sans verrouillage pour double
s sur 4 ou Frontières de 8 octets. Le compilateur devrait alors utiliser des verrous dans les cas non alignés (et renvoyer la valeur booléenne appropriée is_lock_free
, en fonction de l'emplacement mémoire de l'instance d'objet).
is_always_lock_free
?