Frage

Die zweite Woche schrieb ich ein wenig Thread-Klasse und eine unidirektionale Nachricht Rohr Kommunikation zwischen Threads zu ermöglichen (zwei Rohre pro Faden, natürlich, für die bidirektionale Kommunikation). Alles hat gut funktioniert auf meinem Athlon 64 X2, aber ich habe mich gefragt, ob ich auf Probleme stoßen würde, wenn beide Threads zur gleichen Variablen und der lokalen Cache gespeicherten Wert für diese Variable auf jedem Kern nicht synchron war gesucht haben.

Ich weiß, dass die flüchtig Keyword wird eine variable Kraft aus dem Speicher zu aktualisieren, aber gibt es eine Möglichkeit, auf Multi-Core-x86-Prozessoren die Caches aller Kerne zu zwingen, zu synchronisieren? Ist das etwas, was ich befürchten müssen, oder wird flüchtig und die ordnungsgemäße Verwendung von leichten Verriegelungsmechanismen (I _InterlockedExchange wurde mit meiner flüchtigen Rohrvariablen setzen), um alle Fälle behandeln, in denen ich schreiben möchte „lock frei“ Code für Multi-Core-x86-CPUs?

Ich bin bereits bekannt und haben Kritische Abschnitte, Mutexes, Veranstaltungen, und so weiter. Ich frage mich, vor allem, wenn es x86-Spezifika, die ich von denen nicht bewusst bin, Gewalt oder kann verwendet werden, Cache-Kohärenz zu erzwingen.

War es hilfreich?

Lösung

volatile zwingt nur den Code, den Wert neu zu lesen, kann es nicht steuern, wo der Wert von gelesen wird. Wenn der Wert vor kurzem von Ihrem Code gelesen wurde, dann wird es wahrscheinlich im Cache sein, wobei in diesem Fall flüchtig zwingt sie aus dem Cache, nicht aus dem Speicher wieder gelesen werden.

Es gibt nicht viele Cache-Kohärenz-Anweisungen in x86. Es gibt Vorabrufbefehle wie prefetchnta , aber das hat keinen Einfluss auf die Speicher-Bestellung Semantik. Es wird verwendet, indem den Wert L1-Cache implementiert werden, ohne L2 umweltfreundlich, aber die Dinge sind komplizierter für moderne Intel-Design mit einer großen gemeinsamen inklusive L3-Cache.

x86-CPUs verwenden, um eine Variation des MESI-Protokoll (MESIF für Intel, MOESI für AMD) ihre Caches kohärent miteinander (einschließlich der privaten Caches L1 verschiedener Kerne) zu halten. Ein Kern, der eine Cache-Zeile schreiben will, muss andere Kerne zwingen, ihre Kopie davon ungültig zu machen, bevor er seine eigene Kopie von Shared Modifiziert-Zustand ändern kann.


Sie brauchen keine Zaun Anweisungen (wie MFENCE) in einer Thread-Daten zu erzeugen und es in einem anderen auf x86 verbrauchen, weil x86 Lasten / Läden haben erwerben / release Semantik eingebaut. Sie brauchen MFENCE (volle Barriere) sequenzielle Konsistenz zu erhalten. (Eine frühere Version dieser Antwort vorgeschlagen, dass clflush notwendig war, die nicht korrekt ist).

Sie müssen Kompilierung-Neuordnungs zu verhindern, weil C ++ 's Speichermodell ist schwach geordnet. volatile ist eine alte, schlechte Art und Weise, dies zu tun; C ++ 11 std :: Atom ist ein viel besserer Weg schleusenfreien Code zu schreiben.

Andere Tipps

Cache Kohärenz aufgrund des MESI-Protokoll von x86-Prozessoren eingesetzt zwischen den Kernen garantiert. Sie müssen nur über Speicher Kohärenz sorgen, wenn mit externer Hardware zu tun, die den Speicher zugreifen kann, während die Daten noch auf Kerne Caches stationieren. Sieht nicht wie es ist Ihr Fall hier, obwohl, da der Text schon sagt sind Sie in Userland-Programmierung.

Sie müssen nicht über die Cache-Kohärenz sorgen. Die Hardware kümmern, dass. Was können Sie über Themen ist die Leistung kümmern müssen aufgrund dieser Cache-Kohärenz.

Wenn Kern # 1 schreibt auf eine Variable, die alle anderen Kopien der Cache-Zeile in anderen Kernen ungültig (weil es bekommen hat? exklusiver Besitz der Cache-Zeile, bevor Sie den Speicher zu begehen). Wenn Kern # 2 liest die gleiche Variable, es in Cache verpassen (es sei denn, ein Kern # es bereits geschrieben hat, so weit als Shared Cache-Ebene zurück).

Da eine gesamte Cache-Zeile (64 Bytes) aus dem Speicher werden muss, lesen (oder zurück in dem gemeinsam genutzten Cache-Speicher geschrieben und dann von Kern # 2 lesen), wird es einige Performance-Kosten hat. In diesem Fall ist es nicht zu vermeiden. Dies ist das gewünschte Verhalten.


Das Problem ist, dass, wenn Sie mehrere Variablen in der gleichen Cache-Zeile haben, kann der Prozessor mehr Zeit damit verbringen, die Caches synchron zu halten, auch wenn die Kerne Lese / verschiedene Variablen innerhalb der gleichen Cache-Zeile zu schreiben.

Diese Kosten können, indem sie sicher, dass diese Variablen sind nicht in der gleichen Cache-Zeile vermieden werden. Dieser Effekt ist bekannt als False-Sharing , da Sie die Prozessoren zwingen die Werte von Objekten zu synchronisieren, die zwischen Threads nicht wirklich geteilt werden.

Flüchtige wird es nicht tun. In C ++, volatile wirkt sich nur, was Compiler-Optimierungen wie eine Variable in einem Register anstelle des Speichers zu speichern, oder es vollständig zu entfernen.

Sie hat nicht angegeben, welche Compiler Sie verwenden, aber wenn Sie auf Fenster sind, nehmen Sie einen Blick auf diesen Artikel hier . Werfen Sie auch einen Blick auf die ynchronization Funktionen hier . Sie könnten beachten wollen, dass im Allgemeinen volatile nicht genug zu tun, was Sie tun wollen, aber unter VC 2005 und 2008 gibt es Nicht-Standard-Semantik hinzugefügt, die Speichersperren um lesen und schreiben implizierte hinzufügen.

Wenn Sie die Dinge wollen tragbar sein, du wirst eine viel härtere Straße vor Ihnen haben.

Es gibt eine Reihe von Artikeln zu erklären moderne Speicherarchitekturen hier , einschließlich < a href = "http://duartes.org/gustavo/blog/post/intel-cpu-caches" rel = "nofollow noreferrer"> Intel Core2-Caches und viele weitere moderne Architektur Themen.

Die Artikel sind sehr gut lesbar und gut bebildert. Genießen Sie!

Es gibt mehrere Unterfragen in Frage, so werde ich sie zum bestem Wissen und Gewissen beantworten.

  1. Es gibt derzeit keine tragbare Art und Weise Lock-Free-Wechselwirkungen in C ++ zu implementieren. Der C ++ 0x Vorschlag löst dieses Problem durch die atomics Bibliothek eingeführt werden.
  2. Volatile garantiert nicht Unteilbarkeit auf einem Multicore zur Verfügung zu stellen und deren Umsetzung ist herstellerspezifisch.
  3. Auf der x86, brauchen Sie nichts Besonderes zu tun, außer declare Umgebungsvariablen als flüchtiges einige Compiler-Optimierungen zu verhindern, die multithreaded Code brechen. Volatile teilt dem Compiler keine Werte zwischenzuspeichern.
  4. Es gibt einige Algorithmen (Dekker, zum Beispiel), die nicht einmal mit flüchtigen Variablen auf einen x86 funktionieren.
  5. Wenn Sie nicht sicher wissen, dass zwischen dem Threads Zugriff auf Daten vorbei eine wichtige Performance-Engpass in Ihrem Programm ist, bleiben Sie weg von Lock-Free-Lösungen. Verwenden Sie Übergabe von Daten von Wert oder Sperren.

Das folgende ist ein guter Artikel in Bezug auf die Verwendung von volatile w / Gewinde Programme.

Herb Sutter schien einfach empfiehlt , dass zwei Variablen auf separaten Cache-Zeilen befinden sollten. Er tut dies in seiner gleichzeitigen Warteschlange mit Polsterung zwischen seinen Schlössern und Knoten Zeigern.

Edit: Wenn Sie die Intel-Compiler oder GCC verwenden, können Sie die Atom builtins , die ihr Bestes zu tun, scheint den Cache präjudizieren, wenn möglich.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top