Kann mir jemand sagen, ob std :: atomic :: is_lock_free () nicht so statisch wie constexpr ist? Es macht für mich keinen Sinn, es nicht statisch und / oder nicht kontextuell zu haben.
Kann mir jemand sagen, ob std :: atomic :: is_lock_free () nicht so statisch wie constexpr ist? Es macht für mich keinen Sinn, es nicht statisch und / oder nicht kontextuell zu haben.
Antworten:
Wie auf cppreference erklärt :
Alle Atomtypen mit Ausnahme von std :: atomic_flag können mithilfe von Mutexen oder anderen Sperroperationen implementiert werden, anstatt die Anweisungen für die sperrenfreie Atom-CPU zu verwenden. Atomtypen dürfen manchmal auch sperrenfrei sein, z. B. wenn nur ausgerichtete Speicherzugriffe auf einer bestimmten Architektur von Natur aus atomar sind, müssen falsch ausgerichtete Objekte desselben Typs Sperren verwenden.
Der C ++ - Standard empfiehlt (erfordert jedoch nicht), dass sperrfreie atomare Operationen auch adressfrei sind, dh für die Kommunikation zwischen Prozessen unter Verwendung des gemeinsam genutzten Speichers geeignet sind.
Wie von mehreren anderen erwähnt, std::is_always_lock_free
könnte es das sein, wonach Sie wirklich suchen.
Bearbeiten: Zur Verdeutlichung haben C ++ - Objekttypen einen Ausrichtungswert, der die Adressen ihrer Instanzen auf nur bestimmte Vielfache von Zweierpotenzen ( [basic.align]
) beschränkt. Diese Ausrichtungswerte sind für grundlegende Typen implementierungsdefiniert und müssen nicht der Größe des Typs entsprechen. Sie können auch strenger sein als das, was die Hardware tatsächlich unterstützen könnte.
Beispielsweise unterstützt x86 (meistens) nicht ausgerichtete Zugriffe. Sie werden jedoch feststellen, dass die meisten Compiler alignof(double) == sizeof(double) == 8
x86 verwenden, da nicht ausgerichtete Zugriffe eine Reihe von Nachteilen aufweisen (Geschwindigkeit, Caching, Atomizität ...). Aber zB #pragma pack(1) struct X { char a; double b; };
oder alignas(1) double x;
erlaubt es Ihnen, "nicht ausgerichtete" double
s zu haben. Wenn cppreference also von "ausgerichteten Speicherzugriffen" spricht, geschieht dies vermutlich im Hinblick auf die natürliche Ausrichtung des Typs für die Hardware, wobei ein C ++ - Typ nicht in einer Weise verwendet wird, die seinen Ausrichtungsanforderungen widerspricht (was UB wäre).
Hier finden Sie weitere Informationen: Welche tatsächlichen Auswirkungen haben erfolgreiche nicht ausgerichtete Zugriffe auf x86?
Bitte lesen Sie auch die aufschlussreichen Kommentare von @Peter Cordes unten!
alignof(double)==4
. Hat aber std::atomic<double>
immer noch alignof() = 8
anstatt die Ausrichtung zur Laufzeit zu überprüfen. Die Verwendung einer gepackten Struktur, die atomar unterausrichtet, unterbricht den ABI und wird nicht unterstützt. (GCC für 32-Bit-x86 bevorzugt die natürliche Ausrichtung von 8-Byte-Objekten, aber Strukturpackregeln überschreiben diese und basieren nur auf alignof(T)
z. B. i386 System V. G ++ hatte früher einen Fehler, bei dem atomic<int64_t>
eine Struktur möglicherweise nicht atomar ist weil es gerade angenommen hat. GCC (für C nicht C ++) hat immer noch diesen Fehler!)
std::atomic_ref<double>
lehnt jedoch entweder eine Unterausrichtung double
vollständig ab oder überprüft die Ausrichtung zur Laufzeit auf Plattformen, auf denen es legal ist, einfach double
und int64_t
weniger als natürlich ausgerichtet zu sein. (Weil atomic_ref<T>
auf ein Objekt T
alignof(T)
_Atomic int64_t
beim Kompilieren mit Strom zeigt gcc -m32
. Mein Punkt ist jedenfalls, dass echte Compiler keine unterausgerichteten Atomics unterstützen und (noch?) Keine Laufzeitprüfungen durchführen, #pragma pack
oder __attribute__((packed))
nur zu Nicht-Atomizität führen. Objekte melden weiterhin, dass sie sind lock_free
.
is_lock_free()
ist es, Implementierungen zu ermöglichen , die anders funktionieren als aktuelle. mit Laufzeitprüfungen basierend auf der tatsächlichen Ausrichtung, um HW-unterstützte atomare Anweisungen zu verwenden oder eine Sperre zu verwenden.
Sie können verwenden std::is_always_lock_free
is_lock_free
hängt vom tatsächlichen System ab und kann zur Kompilierungszeit nicht ermittelt werden.
Relevante Erklärung:
Atomtypen dürfen manchmal auch sperrenfrei sein, z. B. wenn nur ausgerichtete Speicherzugriffe auf einer bestimmten Architektur von Natur aus atomar sind, müssen falsch ausgerichtete Objekte desselben Typs Sperren verwenden.
std::numeric_limits<int>::max
hängt von der Architektur ab, ist aber statisch und constexpr
. Ich denke, es ist nichts falsch in der Antwort, aber ich kaufe nicht den ersten Teil der Argumentation
is_lock_free
sinnlos ist .
Ich habe Visual Studio 2019 auf meinem Windows-PC installiert und dieses Gerät hat auch einen ARMv8-Compiler. ARMv8 ermöglicht nicht ausgerichtete Zugriffe, aber Vergleiche und Swaps, gesperrte Adds usw. müssen ausgerichtet werden. Und auch reines Laden / reines Speichern unter Verwendung von ldp
oder stp
(Ladepaar oder Speicherpaar von 32-Bit-Registern) ist nur dann garantiert atomar, wenn sie auf natürliche Weise ausgerichtet sind.
Also habe ich ein kleines Programm geschrieben, um zu überprüfen, was is_lock_free () für einen beliebigen Atomzeiger zurückgibt. Also hier ist der Code:
#include <atomic>
#include <cstddef>
using namespace std;
bool isLockFreeAtomic( atomic<uint64_t> *a64 )
{
return a64->is_lock_free();
}
Und dies ist die Demontage von isLockFreeAtomic
|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC
movs r0,#1
bx lr
ENDP
Dies ist nur returns true
, aka 1
.
Diese Implementierung wählt die Verwendung, alignof( atomic<int64_t> ) == 8
damit alle atomic<int64_t>
korrekt ausgerichtet sind. Dies vermeidet die Notwendigkeit von Laufzeitausrichtungsprüfungen bei jedem Laden und Speichern.
(Anmerkung des Herausgebers: Dies ist üblich. Die meisten realen C ++ - Implementierungen funktionieren auf diese Weise. Aus diesem Grund std::is_always_lock_free
ist dies so nützlich: Weil es normalerweise für Typen gilt, bei denen dies is_lock_free()
jemals der Fall ist.)
atomic<uint64_t>
und alignof() == 8
müssen daher die Ausrichtung zur Laufzeit nicht überprüfen. Diese alte API gibt ihnen die Möglichkeit, dies nicht zu tun, aber bei der aktuellen HW ist es viel sinnvoller, nur eine Ausrichtung zu verlangen (ansonsten UB, z. B. Nichtatomarität). Selbst in 32-Bit-Code, in dem int64_t
möglicherweise nur eine 4-Byte-Ausrichtung vorhanden ist, atomic<int64_t>
sind 8 Byte erforderlich. Siehe meine Kommentare zu einer anderen Antwort
alignof
Wert für einen Grundtyp so festlegt wie die "gute" Ausrichtung der Hardware, ist dies is_lock_free
immer der Fall true
(und das wird auch so sein is_always_lock_free
). Ihr Compiler hier macht genau das. Die API ist jedoch vorhanden, sodass andere Compiler andere Aufgaben ausführen können.
alignof(std::atomic<double>) == 1
(es würde also keinen "nicht ausgerichteten Zugriff" im C ++ - Sinne geben, daher kein UB), selbst wenn die Hardware nur sperrfreie atomare Operationen für double
s auf 4 oder 4 garantieren kann 8 Byte Grenzen. Der Compiler müsste dann in den nicht ausgerichteten Fällen Sperren verwenden (und is_lock_free
abhängig vom Speicherort der Objektinstanz den entsprechenden booleschen Wert zurückgeben ).
is_always_lock_free
?