2013-06-06 11 views
18

In seiner Blog Herb Sutter schreibtIst Atom-Dekrementierung teurer als Inkrementieren?

[...] weil die Smart-Pointer-Referenzzähler Inkrementieren in der Regel optimiert werden kann das gleiche wie ein gewöhnlicher Zuwachs in einer optimierten shared_ptr Implementierung zu sein - nur ein gewöhnlicher Zuwachs Anweisung, und keine Zäune, in dem generierten Code.

jedoch das Dekrement muß ein atomares Dekrement sein oder gleichwertig, welche Befehle speziellen Prozessorspeicher erzeugt, die teurer sind in selbst, und dass auf der Oberseite, die auf die Optimierung der umgebenden Kodespeicher fence Beschränkungen induzieren.

Der Text ist über die Umsetzung von shared_ptr und ich bin mir nicht sicher, ob seine Bemerkung gilt nur für diese oder ist im Allgemeinen der Fall. Aus seiner Formulierung entnehme ich, dass es allgemein ist.

Aber wenn ich darüber nachdenke, kann ich nur an "teurere Dekremente" denken, wenn sofort ein if(counter==0) folgt - was wahrscheinlich der Fall ist mit shared_ptr.

Deshalb frage ich mich, wenn der Atom Betrieb ++counter ist (in der Regel) immer schneller als --counter, oder gerade weil ist es if(--counter==0)... mit shared_ptr verwendet?

+1

* "Von seiner Formulierung, die ich erfahre, ist es allgemein" * - ich nehme jedoch das Gegenteil fest. –

+0

Sie können auch die WriteRead-Barriere betrachten, die für den --counter == 0 erforderlich ist und die in der Regel die teuerste ist, da dies die einzige Art von Barriere ist, die direkt nach dem Schreiben der Daten erforderlich ist muss neu gelesen werden, um die Synchronisation zwischen den Kernen zu gewährleisten. Für Zähler ++ schreiben Sie einfach, ohne dass Sie sofort einen synchronisierten Zustand dieses Wertes oder anderer Daten benötigen. –

Antwort

11

Ich glaube, es bezieht sich auf die Tatsache, dass das Inkrement "versteckt" werden kann, wo die "Dekrement und Check" als eine Operation durchgeführt werden muss.

Ich bin nicht bekannt, dass Architektur, wo --counter (oder counter--, asssuming wir reden über einfache Datentypen wie int, char, etc.) ist langsamer als ++counter oder counter++.

+0

Was meinst du mit "versteckt"? – curiousguy

+0

Es ist jedoch genau das Gegenteil von dem, was er sagt. Er sagt ausdrücklich "gewöhnliches Inkrement". Es geht nicht darum, den Wert danach nicht zu überprüfen oder dergleichen. Es geht darum, ein gewöhnliches Inkrement zu verwenden (Cacheline lesen, modifizieren, Cacheline schreiben), das bei Vorhandensein von Nebenläufigkeit Referenzen verliert. Ich bin daher sehr geneigt zu sagen, dass das, was er sagt, einfach falsch ist. Sutter kann auch falsche Dinge sagen, warum nicht. – Damon

16

Er diskutiert das genauer irgendwo, ich denke in seiner atomic<> weapons Präsentation. Grundsätzlich geht es darum, wo im Anwendungsfall shared_ptr die Speicher-Zäune benötigt werden, und nicht um intrinsische Eigenschaften atomarer Inkremente gegen Dekremente.

Der Grund ist, dass Sie nicht wirklich auf die genaue Reihenfolge der Inkremente mit einem ref gezählten Smart Pointer kümmern, solange Sie nicht verpassen, aber mit Dekrementen ist es entscheidend, dass Sie Speicher Barrieren haben, so dass Bei Ihrem letzten Dekrement, das den Löschvorgang auslöst, haben Sie keine Möglichkeit, dass frühere Speicherzugriffe von einem anderen Thread auf das Objekt, dem der Smart-Zeiger gehört, neu angeordnet werden, nachdem der Speicher freigegeben wurde.

+0

Das Gespräch von Herb ist super-Duper! Vielen Dank! +1 – towi

+0

Die "so lange wie Sie keine verpassen" ist jedoch der wichtige Teil. Das und die Vor-Vor-Garantie in Bezug auf das Dekrement (aber das wird durch das atomare Dekrement behandelt). Er sagt ausdrücklich "normales Inkrement", was bei einem gleichzeitigen Szenario einige Inkremente vermisst. Vielleicht möchte er etwas anderes sagen, aber er hat es trotzdem gesagt. – Damon

8

Das Problem, über das Sutter spricht, ist die Tatsache, dass das Referenzzählinkrement keine Folgeaktion für die Korrektheit erfordert. Sie nehmen eine Referenzzahl ungleich null auf eine andere Zahl ungleich null, sodass keine weitere Aktion erforderlich ist. Das Dekrement erfordert jedoch eine Folgeaktion für die Richtigkeit. Das Dekrement nimmt eine von null verschiedene Referenzzahl auf entweder eine Referenzzahl ungleich Null oder Null, und wenn Ihre Dekrementierung der Referenzzählung auf Null geht, müssen Sie eine Aktion ausführen --- insbesondere die Zuordnung des referenzierten Objekts.Diese Dekrement-und-Akt-Dynamik erfordert eine größere Konsistenz, sowohl auf der Fence-Ebene (also wird die Deallokation nicht mit einem anderen Lese-/Schreibzugriff auf einen anderen Kern neu geordnet, den die Speicher/Cache-Management-Logik der CPU neu geordnet hat) als auch auf der Compiler-Ebene (der Compiler ordnet also keine Operationen um die Dekremente herum an, die dazu führen könnten, dass Lese-/Schreibvorgänge um die potenzielle Freigabe neu geordnet werden).

Also, für Sutters beschriebenes Szenario liegt der Unterschied in den Kosten zwischen Inkrement und Dekrement nicht in den fundamentalen Operationen selbst, sondern in den Konsistenzbeschränkungen, die der tatsächlichen Verwendung des Dekrements auferlegt sind (spezifisch auf das Dekrement selbst einwirkend). das gilt nicht für das Inkrement.