Wenn eine Last zwei frühere Speicher überlappt (und die Last nicht vollständig im ältesten Speicher enthalten ist), können moderne Intel- oder AMD x86-Implementierungen von beiden Speicherorten weitergeleitet werden, um die Last zu erfüllen?
Betrachten Sie zum Beispiel die folgende Reihenfolge:
%Vor%Die letzte 2-Byte-Ladung nimmt ihr zweites Byte von dem unmittelbar vorhergehenden Speicher, aber ihr erstes Byte aus dem Speicher davor. Kann diese Ladung über den Speicher weitergeleitet werden oder muss sie warten, bis beide vorherigen Speichervorgänge in L1 festgeschrieben sind?
Beachten Sie, dass ich durch Weiterleitung hier jeden Mechanismus einschließe, der die Lesevorgänge aus Speichern im Speicherpuffer erfüllen kann, anstatt darauf zu warten, dass sie an L1 übergeben werden, auch wenn es ein langsameren Pfad als der beste Fall "aus einem einzelnen Speichervorgang".
Zumindest nicht bei Haswell-, Broadwell- oder Skylake-Prozessoren. Bei anderen Intel-Prozessoren sind die Beschränkungen entweder ähnlich (Sandy Bridge, Ivy Bridge) oder noch enger (Nehalem, Westmere, Pentium Pro / II / II / 4). Bei AMD gelten ähnliche Einschränkungen.
Von Agner Fogs ausgezeichneten Optimierungshandbüchern :
Die Mikroarchitektur von Intel und AMD CPUs
§ 10.12 Weiterleitungsboxen speichern
Der Prozessor kann unter bestimmten Bedingungen einen Speicherschreibvorgang an einen nachfolgenden Lesevorgang von derselben Adresse weiterleiten. Die Speicherweiterleitung funktioniert in folgenden Fällen:
- Wenn auf einen Schreibvorgang von 64 Bits oder weniger gefolgt wird, wird unabhängig von der Ausrichtung ein Lesevorgang derselben Größe und derselben Adresse ausgeführt.
- Wenn auf einen Schreibvorgang von 128 oder 256 Bits gefolgt wird, wird ein Lesevorgang derselben Größe und derselben Adresse vollständig ausgerichtet.
- Wenn auf einen Schreibvorgang von 64 Bits oder weniger ein Lesevorgang kleinerer Größe folgt, der unabhängig von der Ausrichtung vollständig im Schreibadressbereich enthalten ist.
- Wenn ein ausgerichteter Schreibvorgang beliebiger Größe von zwei Lesevorgängen der beiden Hälften oder vier Lesevorgängen der vier Viertel usw. mit ihrer natürlichen Ausrichtung innerhalb des Schreibadressbereichs gefolgt wird.
- Wenn einem ausgerichteten Schreiben von 128 Bits oder 256 Bits ein Lesen von 64 Bits oder weniger folgt, das eine Grenze von 8 Bytes nicht überschreitet.
Eine Verzögerung von 2 Takten tritt auf, wenn der Speicherblock eine 64-Byte-Cachezeilengrenze überschreitet. Dies kann vermieden werden, wenn alle Daten ihre natürliche Ausrichtung haben.
Die Speicherweiterleitung schlägt in den folgenden Fällen fehl:
- Wenn auf einen Schreibvorgang jeder Größe ein größerer Lesevorgang folgt
- Wenn auf einen Schreibvorgang beliebiger Größe ein teilweise überlappender Lesevorgang folgt,
- Wenn auf ein Schreiben von 128 Bits ein kleinerer Wert folgt, der die Grenze zwischen den beiden 64-Bit-Hälften kreuzt
- Wenn einem Schreiben von 256 Bits ein 128-Bit-Lesen folgt, das die Grenze zwischen den beiden 128-Bit-Hälften kreuzt
- Wenn einem Schreiben von 256 Bits ein Lesen von 64 Bits oder weniger gefolgt wird, das eine Grenze zwischen den vier 64-Bit-Vierteln überschreitet
Eine fehlgeschlagene Speicherweiterleitung dauert 10 Taktzyklen mehr als eine erfolgreiche Speicherweiterleitung. Die Strafe ist viel höher - ungefähr 50 Taktzyklen - nach einem Schreiben von 128 oder 256 Bits, das nicht um mindestens 16 ausgerichtet ist.
Hervorhebung hinzugefügt
Die Mikroarchitektur von Intel und AMD CPUs
§ 11.12 Weiterleitungsboxen speichern
Der Skylake-Prozessor kann unter bestimmten Bedingungen einen Speicherschreibvorgang an einen nachfolgenden Lesevorgang von derselben Adresse weiterleiten. Die Speicherweiterleitung ist einen Taktzyklus schneller als bei früheren Prozessoren. Ein Speicherschreiben gefolgt von einem Lesen von der gleichen Adresse dauert im besten Fall 4 Zyklen für Operanden von 32 oder 64 Bits und 5 Taktzyklen für andere Operandengrößen.
Die Speicherweiterleitung hat einen Nachteil von bis zu 3 zusätzlichen Taktzyklen, wenn ein Operand mit 128 oder 256 Bit falsch ausgerichtet ist.
Eine Speicherweiterleitung benötigt normalerweise 4-5 zusätzliche Taktzyklen, wenn ein Operand beliebiger Größe eine Cache-Zeilengrenze überschreitet, d. h. eine Adresse, die durch 64 Bytes teilbar ist.
Ein Schreiben gefolgt von einem kleineren Lesen von der gleichen Adresse hat wenig oder keine Strafe.
Ein Schreiben von 64 Bits oder weniger, gefolgt von einem kleineren Lesen, hat eine Strafe von 1 - 3 Takten, wenn das Lesen offset ist, aber vollständig in dem durch das Schreiben abgedeckten Adressbereich enthalten ist.
Ein ausgerichteter Schreibvorgang von 128 oder 256 Bits gefolgt von einem Lesen von einer oder beiden der zwei Hälften oder der vier Viertel usw. hat wenig oder keine Strafe. Ein partielles Lesen, das nicht in die Hälften oder Viertel passt, kann 11 zusätzliche Taktzyklen dauern.
Ein Lesevorgang, der größer als der Schreibvorgang ist, oder ein Lesevorgang, der sowohl geschriebene als auch ungeschriebene Bytes abdeckt , benötigt etwa 11 zusätzliche Taktzyklen.
Hervorhebung hinzugefügt
Ein gemeinsamer Punkt in Mikroarchitekturen, auf den Agner Fogs Dokument hinweist, ist, dass die Speicherweiterleitung wahrscheinlicher ist, wenn der Schreibvorgang ausgerichtet wurde und die Lesevorgänge Hälften oder Quartale sind der geschriebene Wert.
Ein Test mit der folgenden engen Schleife:
%Vor% Zeigt an, dass der ld_blocks.store_forward
PMU-Zähler tatsächlich erhöht wird. Dieses Ereignis ist wie folgt dokumentiert:
ld_blocks.store_forward [Dieses Ereignis zählt wie Viele Male hat die Ladeoperation die wahre Block-on-Store-Blockierung bekommen Code verhindert Speicherweiterleitung.Dies schließt Fälle ein, wenn - Vorhergehender Speicherkonflikt mit dem Ladevorgang (unvollständige Überlappung)
Store-Weiterleitung ist aufgrund von U-arch-Beschränkungen nicht möglich
vorhergehende Sperre RMW-Operationen werden nicht weitergeleitet
store hat das No-Forward-Bit gesetzt (uncacheable / page-split / masked stores)
blockierende Speicher werden verwendet (meistens Zäune und Port-I / O)
Dies zeigt an, dass die Weiterleitung des Speichers tatsächlich fehlschlägt, wenn ein Lesevorgang den letzten früheren Speicher teilweise überlappt (auch wenn er vollständig enthalten ist, wenn noch frühere Speicher berücksichtigt werden).
In-order Atom kann diese Speicherweiterleitung möglicherweise ohne Verzögerung durchführen.
Agner Fog erwähnt diesen Fall nicht speziell für Atom, aber im Gegensatz zu allen anderen CPUs kann er mit einer 1c-Latenz von einem Speicher zu einer breiteren oder anders ausgerichteten Last weiterspeichern. Die einzige Ausnahme, die Agner fand, war an den Grenzen der Cache-Zeile, wo Atom schrecklich ist (16-Zyklus-Strafe für eine CL-Split-Last oder einen Speicher, selbst wenn keine Speicherweiterleitung beteiligt ist).
Kann diese Ladung über den Speicher weitergeleitet werden oder muss gewartet werden, bis beide früheren Speichervorgänge in L1 festgeschrieben sind?
Hier gibt es ein Terminologieproblem. Viele Leute interpretieren "Kann diese Datei über das Geschäft weitergeleitet werden?" Und fragen, ob dies mit so geringer Latenz passieren kann, als wenn alle Anforderungen für die Fast-Path-Weiterleitung erfüllt sind, wie in der @ IWill-Antwort aufgeführt. (Wo alle geladenen Daten aus dem letzten Speicher stammen, um die Last zu überlappen, und andere relative / absolute Werte Ausrichtungsregeln sind erfüllt).
Ich dachte zuerst, dass Sie die dritte Möglichkeit der langsameren aber immer noch (fast?) festen Latenzweiterleitung verpassen würden, ohne auf das Festschreiben an L1D zu warten, z.B. mit einem Mechanismus, der den gesamten Speicherpuffer (und möglicherweise Lasten von L1D) abkratzt, falls Agner Fog und Intels Optimierungshandbuch "Speicherweiterleitungsfehler" aufrufen.
Aber jetzt sehe ich, dass diese Formulierung beabsichtigt war, und Sie wollen wirklich fragen, ob die dritte Option existiert oder nicht.
Vielleicht möchten Sie einige davon in Ihre Frage bearbeiten. Zusammenfassend sind die drei möglichen Optionen für Intel x86-CPUs:
Zusätzliche (aber begrenzte) Latenz, um den gesamten Speicherpuffer zu durchsuchen und die korrekten Bytes (nach Programmreihenfolge) zusammenzustellen und (falls erforderlich oder immer?) von L1D zu laden, um Daten für alle Bytes zu liefern, die nicht in letzter Zeit vorhanden waren gespeichert.
Dies ist die Option, von der wir nicht sicher sind, ob sie existiert .
Es muss auch auf alle Daten von Filialdatendateien warten, die ihre Eingaben noch nicht bereit haben, da sie die Programmreihenfolge einhalten müssen. Es kann einige Informationen über spekulative Ausführung mit unbekannter Geschäftsadresse geben (z. B. Raten, dass sie sich nicht überschneiden), aber ich vergesse.
Warten Sie, bis alle überlappenden Stores auf L1D festgelegt sind, und laden Sie dann von L1D.
Einige echte x86-CPUs könnten in einigen Fällen darauf zurückgreifen, aber sie verwenden möglicherweise immer Option 2, ohne eine StoreLoad-Barriere einzuführen. (Denken Sie daran, dass x86-Speicher in der Programmreihenfolge festgeschrieben werden müssen und Ladevorgänge in der Programmreihenfolge erfolgen müssen. Dies würde effektiv den Speicherpuffer wie mfence
an diesen Punkt entleeren, obwohl spätere Ladevorgänge an andere Adressen noch spekulativ speichern oder weiterleiten könnten Nimm einfach Daten von L1D.)
Das Sperrschema, das in Kann x86 einen schmalen Laden mit einer größeren Ladung, die ihn vollständig enthält, neu ordnen? würde funktionieren, wenn der Speicherweiterleitungsfehler eine Leerung an L1D erfordert. Da es auf echter Hardware ohne mfence
nicht funktioniert, ist dies ein starker Beweis dafür, dass reale x86-CPUs Daten aus dem Speicherpuffer mit Daten von L1D zusammenführen. Also Option 2 existiert und wird in diesem Fall verwendet.
Siehe auch Linus Torvalds 'Erklärung, dass x86 diese Art der Nachbestellung wirklich zulässt , als Antwort auf jemand anderen wer hat die gleiche Sperridee wie diese SO-Frage vorgeschlagen.
Ich habe nicht getestet, ob Fehler bei der Speicherweiterleitung / Blockierung von Stores variabel sind, aber wenn nicht, bedeutet dies, dass der gesamte Speicherpuffer überprüft wird, wenn die Weiterleitung im besten Fall nicht funktioniert.
Hoffentlich wird jemand antworten What sind die Kosten der fehlgeschlagenen Store-to-Load-Weiterleitung auf x86? , die genau das fragt. Ich werde es tun, wenn ich dazu komme.
Agner Fog erwähnt immer nur eine einzige Zahl für Store-Forwarding-Strafen und sagt nicht, dass es größer ist, wenn sich Cache-Miss-Stores vor den Läden befinden, die nicht weiterleiten konnten. (Dies würde zu einer großen Verzögerung führen, da Speicher aufgrund des stark geordneten Speichermodells von x86 der Reihenfolge nach in L1D übergeben werden müssen.) Er sagt auch nichts darüber, dass Daten von 1 Speicher + L1D vs. Teile von zwei oder mehr Läden, also würde ich vermuten, dass es auch in diesem Fall funktioniert.
Ich vermute, dass "gescheiterte" Speicherweiterleitung häufig genug ist, dass es den Transistoren wert ist, sie schneller zu verarbeiten, als nur die Speicherwarteschlange zu leeren und von L1D neu zu laden.
Zum Beispiel gcc versucht nicht speziell, Store-Forwarding-Stände zu vermeiden , und einige seiner Idiome verursachen sie (zB __m128i v = _mm_set_epi64x(a, b);
in 32-Bit-Code speichert / lädt auf den Stack, was ist in den meisten Fällen schon die falsche Strategie für die meisten CPUs, daher dieser Fehlerbericht) .Es ist nicht gut, aber die Ergebnisse sind normalerweise nicht katastrophal, AFAIK.
Tags und Links optimization assembly x86 performance micro-optimization