Beachten Sie, dass AMD Zen auch einen uop-Cache hat, aber weniger über seine internen Daten bekannt ist. Sie fragen also speziell nach Intels uop-Cache in der Sandybridge-Familie.
Laut Tests von Agner Fog ( https://www.agner.org/optimize/, speziell in seinem microarch pdf) ist es virtuell angesprochen (VIVT), wodurch die Latenz / Leistung von iTLB-Suchen für Uop-Cache-Treffer gespart wird. Und es ist möglich, die iTLB sehr eng in den L1i-Cache zu integrieren, wie es für einen VIPT-L1-Cache üblich ist.
(auch verwandt: Welche Cache-Mapping-Technik wird im Intel Core i7-Prozessor verwendet? für eine Zusammenfassung dieses Caches und anderer Caches und https://stackoverflow.com/tags/x86/info für weitere Links zu Performance- / Uarch-Verbindungen.)
Sobald ein 32-Byte-Fenster decodiert wurde
Hier haben Sie in Ihrem Denkprozess einen Fehler gemacht.
Der uop-Cache speichert nur uops, die auf dem Pfad der (spekulativen) Ausführung dekodiert werden. x86-Anweisungen können nur dann korrekt decodiert werden, wenn Sie den richtigen Startpunkt kennen. Die Bytes nach einer unbedingten Bedingung sind jmp
möglicherweise überhaupt nicht der Beginn einer Anweisung.
Außerdem möchten Sie den uop-Cache nicht mit vielen Einzelbyte-Füllanweisungen zwischen Funktionen verschmutzen (z. B. 0x90 NOP oder 0xcc
int3
von MSVC verwendet). Oder im Allgemeinen mit "kalten" Anweisungen, die während einer normalen Ausführung nach einer übernommenen Verzweigung nicht erreicht werden. Eine Uop-Cache-Zeile endet früh mit einem unbedingten Sprung oder mit einem call
.
Bei den älteren Dekodern handelt es sich entweder um Dekodieranweisungen, die die CPU tatsächlich ausführen soll (sie werden zur späteren Wiederverwendung in den uop-Cache eingespeist, und die IDQ wird direkt zur sofortigen Verwendung verwendet), oder sie werden ausgeschaltet . Im Gegensatz zu P4 sind die Altdecoder nicht schwach. Sie sind den Decodern in Core2 / Nehalem ähnlich, daher ist die Ausführung von L1i im Allgemeinen in Ordnung, außer im Code mit hohem Durchsatz und einer großen durchschnittlichen Befehlsgröße. Sie müssen nicht vorab versuchen, "Spuren aufzubauen". (Der uop-Cache ist sowieso kein Trace-Cache; er folgt Sprüngen nicht. Er versucht jedoch nicht, den uop-Cache für alle 32 Befehlsbytes aufzufüllen, die sofort zwischengespeichert werden könnten .)
Interessanterweise sagt Agner: " Ein und derselbe Code kann mehrere Einträge im μop-Cache haben, wenn er mehrere Sprungeinträge hat. "
Meine beste Vermutung, wie die Cache-Lookup-Maschine tatsächlich funktioniert:
Gegeben eine virtuelle 64-Bit-Adresse, von der Code abgerufen werden soll:
- Die niedrigen 5 Bits sind der Versatz relativ zu einer 32-Byte-Grenze.
- Die nächsten 5 Bits sind ein Index. Nicht 6 Bits für 64-Byte-L1i-Leitungen; Abrufen aus dem uop-Cache kümmert sich nicht direkt darum.
- Die höheren Bits (bis zu Bit 48) sind das Tag.
Verwenden Sie den 5-Bit-Index, um einen Satz auszuwählen.
Holen Sie alle 8 Möglichkeiten aus diesem Satz (Tag + Metadaten und auch Daten parallel, da dies ein Hochleistungscache ist).
Vergleichen Sie für alle 8 Möglichkeiten parallel:
- Tag-Bits stimmen alle überein
- Offset liegt innerhalb des Start- + Längenbereichs von x86-Maschinencode, so dass uops uops zwischengespeichert werden. (Ein Weg kann Uops nur für einen zusammenhängenden Block von x86-Maschinencode zwischenspeichern).
Höchstens 1 Weg in dem Satz hat beide Bedingungen für eine gegebene Befehlsadresse. Wenn es einen gibt, ist dies Ihr Treffer, und Sie können Uops von dem einen Weg abrufen, der übereinstimmt. (Wie bei einem regulären Byte-Cache, außer dass Sie die Metadaten überprüfen müssen, um auszuwählen, von welchem Uop abgerufen werden soll, wenn Sie mitten in einen Weg gesprungen sind.)
Dies ist eine Vermutung, die davon abhängt, wie der uop-Cache abschneidet und wann er wegwirft. Aber es kann Ihnen helfen, ein nützliches Denkmodell davon zu bekommen.
Beachten Sie, dass die Adresse nicht auf 16 Byte ausgerichtet sein muss. Es muss Verzweigungsziele, die nicht ausgerichtet sind, sowie geradlinigen Code mit Befehlsgrenzen, die nicht mit 32-Byte-Grenzen übereinstimmen, effizient unterstützen. (Wie ich am besten sagen kann, werden Anweisungen, die eine 32-Byte-Grenze überschreiten, in einer Uop-Cache-Weise für die Startadresse der Anweisung zwischengespeichert, selbst wenn sie in der nächsten L1i-Cachezeile über eine 64-Byte-Grenze endet.)
L1i-Abrufblöcke / Vordecodierung für die Befehlslänge sind aufeinander abgestimmt, aber die vollständige Decodierung in den Altdecodierern arbeitet mit bis zu 16 Bytes jeder beliebigen Ausrichtung, die aus der Warteschlange zwischen Vordecodierung und Decodierung entnommen werden. Das Ausrichten von Schleifeneintrittspunkten an bestimmten Ausrichtungsgrenzen ist weniger wichtig als früher.
Dann gibt es wohl eine Überprüfung, dass die Abrufadresse genau mit einer der Befehlsstartadressen übereinstimmt. Dies wird nicht effizient unterstützt, da nur verschleierter Code dieselben Bytes auf zwei verschiedene Arten decodiert.
Der uop-Cache kann nicht in beide Richtungen gleichzeitig cachen. Wenn dies erkannt wird, muss die CPU auf die älteren Decoder zurückgreifen und die uop-Cache-Wege für diesen 32B-Block (der bereits mit dem Tag-Komparator erkannt wurde) auswerfen.
Dann kann der Uop-Cache neu gefüllt werden, wenn Uops ab diesem Punkt dekodiert werden.
Ähnliches passiert, wenn 3 Möglichkeiten bereits belegt sind, es jedoch mehr Uops aus demselben 32B-Block von x86-Maschinencode gibt. Der uop-Cache gibt alle drei Wege für diesen Block aus. (Ich bin nicht sicher, ob er sich daran erinnert, sie nicht für das nächste Mal im Cache zu speichern, oder ob er nur jedes Mal den Cache aufbaut und bei Überschreiten des Limits wegwirft, nop
z. B. in einer Schleife mit 20 Einzelbyte- Anweisungen. )
Weitere Informationen zu diesem Fall finden Sie unter Branch-Ausrichtung für Schleifen, die mikro-codierte Anweisungen für Intel SnB-Prozessoren enthalten . Beachten Sie, dass mikro-codierte Anweisungen den div
gesamten Uop-Cache alleine beanspruchen und leicht dazu führen können, dass alle drei Möglichkeiten erfüllt werden und DSB-zu-MITE-Schalter ausgelöst werden (Uop-Cache zu Legacy-Decodierschaltern kann eine 1 erzeugen.) Zyklusblase im Frontend).
Diese Q & A enthält viele detaillierte Experimente und Schlussfolgerungen darüber, wie uops zwischengespeichert werden. Nicht so sehr, wie der uop-Cache physisch implementiert wird. Das ist nur eine Vermutung meinerseits hier.
Beachten Sie auch, dass Intel-CPUs vor Skylake dem IDQ nur 4 Uops aus dem Uop-Cache hinzufügen können, aber Engpässe bestehen nicht, wenn der Uop-Cache 3 oder 6 Uops anstelle von 4 hat. Also IDK, wenn es eine Art gibt der Pufferung für nicht verzweigten uop-Abruf. Dies ist ein bisschen ein Rätsel. Man könnte erwarten, dass fetch in 4, 2, 4, 2 geht, wenn man aus vollen Zeilen von jeweils 6 Uops abruft, aber wir sehen keinen solchen Engpass für Schleifen, der vom Uop-Cache mit 2 ausgeführt wird -Byte Anweisungen wie xor eax,eax
. Intel hat angegeben, dass der Uop-Cache Uops nur von einem Weg pro Zyklus abrufen kann. Das 4-Uop-Limit ist möglicherweise nur für das Hinzufügen zum IDQ gedacht, nicht für das Lesen aus dem Uop-Cache in einen Merge-Puffer.