Für etwas Einfaches wie einen Zähler, wenn mehrere Threads die Anzahl erhöhen. Ich habe gelesen, dass Mutex-Sperren die Effizienz verringern können, da die Threads warten müssen. Für mich wäre ein Atomzähler am effizientesten, aber ich habe gelesen, dass es sich im Grunde genommen im Grunde genommen um ein Schloss handelt. Ich bin also verwirrt, wie einer effizienter sein kann als der andere.
75
Antworten:
Atomic Operations nutzen die Prozessorunterstützung (Anweisungen vergleichen und austauschen) und verwenden überhaupt keine Sperren, wohingegen Sperren stärker vom Betriebssystem abhängig sind und beispielsweise unter Win und Linux eine unterschiedliche Leistung erbringen.
Sperren setzen die Thread-Ausführung tatsächlich aus, wodurch CPU-Ressourcen für andere Aufgaben frei werden, aber beim Stoppen / Neustarten des Threads ein offensichtlicher Overhead beim Kontextwechsel entsteht. Im Gegenteil, Threads, die atomare Operationen versuchen, warten nicht und versuchen es bis zum Erfolg (sogenanntes Besetzt-Warten), sodass sie keinen Overhead für die Kontextumschaltung verursachen, aber auch keine CPU-Ressourcen freisetzen.
Zusammenfassend lässt sich sagen, dass atomare Operationen im Allgemeinen schneller sind, wenn die Konkurrenz zwischen Threads ausreichend gering ist. Sie sollten auf jeden Fall ein Benchmarking durchführen, da es keine andere zuverlässige Methode gibt, um zu wissen, was der geringste Overhead zwischen Kontextwechsel und Warten auf Besetzt ist.
quelle
Wenn Sie einen Zähler haben, für den atomare Operationen unterstützt werden, ist dieser effizienter als ein Mutex.
Technisch gesehen sperrt das Atom den Speicherbus auf den meisten Plattformen. Es gibt jedoch zwei verbessernde Details:
quelle
Eine minimale (standardkonforme) Mutex-Implementierung erfordert zwei Grundbestandteile:
Es gibt keine Möglichkeit, es einfacher zu machen, da die Beziehung zum Synchronisieren mit dem C ++ - Standard dies erfordert.
Eine minimale (korrekte) Implementierung könnte folgendermaßen aussehen:
Aufgrund seiner Einfachheit (es kann den Ausführungsthread nicht aussetzen) ist es wahrscheinlich, dass diese Implementierung bei geringen Konflikten a übertrifft
std::mutex
. Aber selbst dann ist leicht zu erkennen, dass jedes durch diesen Mutex geschützte Ganzzahlinkrement die folgenden Operationen erfordert:atomic
Geschäft, um den Mutex freizugebenatomic
Vergleichen und Austauschen (Lesen, Ändern, Schreiben), um den Mutex zu erhalten (möglicherweise mehrmals)Wenn Sie dies mit einem Standalone vergleichen, der mit einem
std::atomic<int>
einzelnen (bedingungslosen) Lese-, Änderungs- und Schreibvorgang (z. B.fetch_add
) inkrementiert wird , ist zu erwarten, dass eine atomare Operation (unter Verwendung des gleichen Ordnungsmodells) den Fall übertrifft, in dem ein Mutex vorliegt benutzt.quelle
Atomic Integer ist dort ein Objekt im Benutzermodus, da es viel effizienter ist als ein Mutex, der im Kernelmodus ausgeführt wird . Der Bereich der atomaren Ganzzahl ist eine einzelne Anwendung, während der Bereich des Mutex für alle auf dem Computer ausgeführten Software gilt.
quelle
Die atomaren Variablenklassen in Java können die vom Prozessor bereitgestellten Vergleichs- und Auslagerungsanweisungen nutzen.
Hier ist eine detaillierte Beschreibung der Unterschiede: http://www.ibm.com/developerworks/library/j-jtp11234/
quelle
Die meisten Prozessoren unterstützen ein atomares Lesen oder Schreiben und häufig ein atomares cmp & swap. Dies bedeutet, dass der Prozessor selbst den neuesten Wert in einer einzelnen Operation schreibt oder liest und im Vergleich zu einem normalen Ganzzahlzugriff möglicherweise einige Zyklen verloren gehen, zumal der Compiler nicht annähernd so gut wie normal für atomare Operationen optimieren kann.
Auf der anderen Seite ist ein Mutex eine Anzahl von Codezeilen, die eingegeben und verlassen werden müssen, und während dieser Ausführung sind andere Prozessoren, die auf denselben Speicherort zugreifen, vollständig blockiert, was eindeutig einen großen Aufwand für sie bedeutet. In nicht optimiertem High-Level-Code sind der Mutex-Ein- / Ausgang und der Atom-Funktionsaufruf. Bei Mutex wird jedoch jeder konkurrierende Prozessor gesperrt, während Ihre Mutex-Eingabefunktion zurückkehrt und Ihre Exit-Funktion gestartet wird. Für Atomic ist nur die Dauer der eigentlichen Operation gesperrt. Durch die Optimierung sollten diese Kosten gesenkt werden, jedoch nicht alle.
Wenn Sie versuchen, ein Inkrement zu erstellen, unterstützt Ihr moderner Prozessor wahrscheinlich das atomare Inkrementieren / Dekrementieren, was großartig sein wird.
Ist dies nicht der Fall, wird es entweder mit dem Prozessor Atomic Cmp & Swap oder mit einem Mutex implementiert.
Mutex:
Atomic cmp & Swap:
Diese zweite Version hat also eine Schleife [falls ein anderer Prozessor den Wert zwischen unseren atomaren Operationen erhöht, sodass der Wert nicht mehr übereinstimmt und das Inkrement falsch wäre], die lang werden kann [wenn es viele Konkurrenten gibt], aber im Allgemeinen immer noch schneller sein sollte als die Mutex-Version, aber die Mutex-Version kann es diesem Prozessor ermöglichen, die Task zu wechseln.
quelle
Mutex
ist eine Semantik auf Kernel-Ebene, die auch auf der Seite gegenseitigen Ausschluss bietetProcess level
. Beachten Sie, dass dies hilfreich sein kann, um den gegenseitigen Ausschluss über Prozessgrenzen hinweg und nicht nur innerhalb eines Prozesses (für Threads) zu erweitern. Es ist teurer.Atomic Counter
AtomicInteger
basiert beispielsweise auf CAS und versucht normalerweise, die Operation auszuführen, bis sie erfolgreich ist. Grundsätzlich rasen oder konkurrieren in diesem Fall Threads, um den Wert atomar zu erhöhen / zu verringern. Hier sehen Sie möglicherweise gute CPU-Zyklen, die von einem Thread verwendet werden, der versucht, mit einem aktuellen Wert zu arbeiten.Da Sie den Zähler beibehalten möchten, ist AtomicInteger \ AtomicLong das Beste für Ihren Anwendungsfall.
quelle