2012-10-15 4 views
37

Ich versuche eine dumme Version einer Spin-Lock zu erstellen. Beim Surfen im Web stieß ich auf eine Assembly-Anweisung namens "PAUSE" in x86, die einem Prozessor anzeigt, dass auf dieser CPU gerade ein Spinlock läuft. Die Intel-Anleitung und weitere Informationen zur Verfügung steht, dassWas ist der Zweck der "PAUSE" Anweisung in x86?

Der Prozessor diesen Hinweis verwendet die Speicher, um Verletzung in die meisten Situationen zu vermeiden, die stark Prozessorleistung verbessert. Für aus diesem Grund empfiehlt es sich, eine PAUSE-Anweisung in alle Spin-Warteschleifen platziert werden. Die Dokumentation erwähnt auch, dass "Warte (einige Verzögerung)" ist die Pseudo-Implementierung der Anweisung.

Die letzte Zeile des obigen Absatzes ist intuitiv. Wenn es mir nicht gelingt, das Schloss zu greifen, muss ich einige Zeit warten, bevor ich das Schloss wieder anfasse.

Was aber meinen wir mit Speicherordnungsverletzung im Falle einer Spinlock? Bedeutet "Memory Order Violation" die falsche spekulative Laden/Speichern der Anweisungen nach Spin-Lock?

Die Spin-Lock-Frage wurde bereits beim Stack-Überlauf gestellt, aber die Frage der Speicherordnungsverletzung bleibt unbeantwortet (zumindest für mein Verständnis).

Antwort

58

Man stelle mir vor, wie der Prozessor einen typischen Spin-Warteschleife durchführen würde: wird vorhersage

1 Spin_Lock: 
2 CMP lockvar, 0 ; Check if lock is free 
3 JE Get_Lock 
4 JMP Spin_Lock 
5 Get_Lock: 

Nach einigen Iterationen der Verzweigungsprädiktor, dass die bedingte Verzweigung (3) wird nie und die Pipeline genommen wird, fülle mit CMP-Anweisungen (2). Dies geht weiter, bis schließlich ein anderer Prozessor eine Null an lockvar schreibt. An diesem Punkt haben wir die Pipeline voll von spekulativen (d. H. Noch nicht festgeschriebenen) CMP-Befehlen, von denen einige bereits lockvar gelesen haben und ein (falsches) von null verschiedenes Ergebnis an die folgende bedingte Verzweigung (3) gemeldet haben (ebenfalls spekulativ). Dies ist der Fall, wenn die Speicherordnungsverletzung auftritt. Wann immer der Prozessor ein externes Schreiben (ein Schreiben von einem anderen Prozessor) "sieht", sucht er in seiner Pipeline nach Instruktionen, die spekulativ auf dieselbe Speicherstelle zugegriffen haben und noch nicht festgeschrieben haben. Wenn solche Anweisungen gefunden werden, ist der spekulative Zustand des Prozessors ungültig und wird mit einem Pipeline-Flush gelöscht.

Leider wird sich dieses Szenario (sehr wahrscheinlich) jedes Mal wiederholen, wenn ein Prozessor auf eine Drehsperre wartet und diese Sperren viel langsamer machen als sie eigentlich sein sollten.

Geben Sie den PAUSE-Befehl:

1 Spin_Lock: 
2 CMP lockvar, 0 ; Check if lock is free 
3 JE Get_Lock 
4 PAUSE   ; Wait for memory pipeline to become empty 
5 JMP Spin_Lock 
6 Get_Lock: 

Die Anweisung pausiert "de-Pipeline" der Speicher ausliest, so dass die Rohrleitung mit dem spekulativen CMP nicht gefüllt ist (2) Anweisungen wie in dem ersten Beispiel. (Dh, es könnte die Pipeline blockieren, bis alle älteren Speicherbefehle festgelegt sind.) Da die CMP-Befehle (2) sequentiell ausgeführt werden, ist es unwahrscheinlich (dh das Zeitfenster ist viel kürzer), dass ein externer Schreibvorgang nach dem Lesen des CMP-Befehls (2) erfolgt lockvar, aber bevor der CMP festgeschrieben wird.

Natürlich wird "de-pipelining" auch weniger Energie im Spin-Lock verschwenden und im Falle von Hyperthreading wird es keine Ressourcen verschwenden, die der andere Thread besser nutzen könnte. Auf der anderen Seite gibt es immer noch eine Verzweigungsfehlvorhersage, die darauf wartet, vor jedem Schleifenexit aufzutreten. Intels Dokumentation schlägt nicht vor, dass PAUSE diese Pipeline-Flush beseitigt, aber wer weiß ...

+0

(+1) Danke für eine tolle Antwort! Was ich nicht vollständig verstehe, ist das, was eine Pipeline in solch einer Situation zu einem so hohen Preis macht, da all diese spekulativen Lesungen und spekulativen bedingten Verzweigungen ohnehin nutzlos sind? Gibt es auch eine Möglichkeit, die Kosten für den Flush zu quantifizieren? – NPE

+4

@NPE Die Zeit bis zur Wiederherstellung nach einem Flush hängt von der Mikroarchitektur ab. Prozessoren mit längeren Pipelines (wie Core 2) leiden offensichtlich mehr als diejenigen mit kürzeren Pipelines (wie Atom). Im Fall eines Prozessors mit Hyperthreading entfernen jedoch alle "nutzlos" ausgeführten Anweisungen Ressourcen von dem anderen Thread auf demselben Kern. Der PAUSE-Befehl gibt die CPU im Wesentlichen an den anderen Thread ab. Während also die Kosten für den gesperrten Thread "nur" zwei Pipelinespülungen sind, können die Kosten für den anderen Thread viel bedeutender sein (abhängig davon, wie viel Zeit innerhalb der Sperre verbracht wird). –

+0

Da die CMP-Befehle (2) sequentiell ausgeführt werden, ist es unwahrscheinlich (d. H. Das Zeitfenster ist viel kürzer), dass ein externer Schreibvorgang auftritt, nachdem der CMP-Befehl (2) lockvar gelesen hat, aber bevor der CMP festgeschrieben wird. Könnten Sie das bitte erklären? Was meinst du mit Begehen? – KodeWarrior