Wie wird der Mikroop-Cache markiert?

552
Lewis Kelsey

In dem Artikel von Real World Technologies über " Sandy Bridge Microarchitecture " von Intel :

„Der Uop-Cache von Sandy Bridge ist in 32 Sätze und 8 Wege mit 6 Uops pro Zeile unterteilt, was eine Gesamtkapazität von 1,5 KB ermöglicht. Der uop-Cache ist streng im L1-Instruktionscache enthalten. Jede Zeile enthält auch Metadaten, einschließlich der Anzahl der gültigen uops in der Zeile und der Länge der x86-Anweisungen, die der uop-Cache-Zeile entsprechen. Jedes 32B-Fenster, das in den Uop-Cache abgebildet wird, kann 3 der 8 Möglichkeiten in einem Satz überspannen, und zwar für maximal 18 Uops - ungefähr 1,8 B / Uop. Wenn ein 32B-Fenster mehr als 18 Uops hat, kann es nicht in den Uop-Cache passen und muss das herkömmliche Front-End verwenden. Mikrocodierte Anweisungen werden nicht im uop-Cache gespeichert und stattdessen durch einen Zeiger auf den Mikrocode-ROM und optional die ersten uops dargestellt. “

'Jedes 32B-Fenster (aus dem Befehls-Cache) wird in den uop-Cache abgebildet und kann 3 der 8 Möglichkeiten eines Satzes umfassen.'

Nehmen wir also an, wir haben ein 32B-Befehlsfenster, das die Hälfte einer L1-Befehls-Cachezeile ist. In dieser Zeile wären nur die Versatzbits verschieden, aber die Markierungs- und Setzbits wären für alle Bytes in der Zeile gleich.

Nachdem ein 32-Byte-Fenster decodiert wurde, werden die Uops mit derselben virtuellen Adresse, die zum Abrufen des 16-Byte-Abrufblocks aus dem L1-Befehls-Cache verwendet wurde, in den Uop-Cache eingegeben (damit sie an jedem 32B-Rand parallel abgetastet werden können).

Es besagt, dass diese Uops 3 der 8 Wege in einem Set umfassen können, aber das würde bedeuten, dass sie die gleichen Set-Bits, aber unterschiedliche Tag-Bits haben müssten, um im selben Set zu landen (was bedeutet, dass sie sich nicht in diesem Set befinden würden) Dieselbe Zeile im L1I - Cache), bedeutet dies, dass der Uop - Cache etwas anders angeordnet ist, eine einzelne virtuelle Adresse am Zeilenanfang und die Uops nur den nächsten Weg im Satz und den nächsten Weg im einstellen. Wie wird sichergestellt, dass das nächste 32B-Befehlsfenster, das immer noch das gleiche Tag und die gleichen Satzbits, aber unterschiedliche Offsetbits (zweite Hälfte der 64B-Zeile in L1I) hätte, auf den vierten Weg dieses Satzes abgebildet wird.

Postulation : Der uop-Cache-Weg wird mit einem physischen Tag mit virtuellem Index markiert, der nächste mit nichts, der dritte mit nichts, der vierte mit einem virtuellen Index / physischen Tag. Der Unterschied besteht darin, dass sich der Versatz von 0 in geändert hat 32, so kann im Wesentlichen ein Weg unter Verwendung verschiedener Offset-Bits im Gegensatz zu der Art und Weise, wie der L1I-Cache markiert wird, ausgewählt werden: wobei die Offset-Bits als Offset für die Cache-Zeile dienen.

Kann jemand das Layout von uop-Caches klären oder wie dieses Tagging tatsächlich funktioniert?

3
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 Mikroarch-PDF) ist es virtuell angesprochen (VIVT), wodurch die Latenz / Leistung von iTLB-Lookups gespart wird. Peter Cordes vor 5 Jahren 0

1 Antwort auf die Frage

1
Peter Cordes

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 jmpmö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 int3von 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, nopz. 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 divgesamten 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.

Vielen Dank dafür: "Der Code wird vom Doppelpuffer in Blöcken an die Decoder weitergegeben, die ich IFETCH-Blöcke (Instruction-Fetch-Blöcke) nennen werde. Die IFETCH-Blöcke sind bis zu 16 Byte lang. In den meisten Fällen lässt die Befehlsabrufeinheit jeden IFETCH-Block an einer Befehlsgrenze statt an einer 16-Byte-Grenze beginnen. ' - microarchitecture.pdf. Es heißt "bis zu 16 Bytes", zur Klarstellung bedeutet dies, dass immer vollständige Anweisungen enthalten sind. Wenn also 5, 5, 4, 4 die ersten 3 zusammen packen und als 14-Byte-Block senden die nächsten 4 beginnen in einem neuen Block ..? Lewis Kelsey vor 5 Jahren 0
..so ist der Durchsatz der Abrufeinheit nicht immer 16 Bytes Lewis Kelsey vor 5 Jahren 0
@LewisKelsey: ach, ich habe vergessen, dass vor der Dekodierung Pufferung durchgeführt wurde, aber es ist sinnvoll, den Durchsatz zu verbessern und mehr aus der auf 16 Bytes beschränkten, stromfressenden Hardware zu machen. Denken Sie jedoch daran, dass die CPU nicht weiß, wo die Anweisungen enden, bevor * vor * der * Decodierung * oder wenn die Verzweigungsvorhersage sagt, dass es eine Verzweigung gibt, deren Ende bekannt ist. Andernfalls liefert das Frontend 16 Byte an die Vordecoder. Wenn das letzte Byte die Mitte eines Inn ist, ist der Start des nächsten Vordecodierungsblocks der Start dieses Befehls (er hängt bis zum nächsten Zyklus herum). Peter Cordes vor 5 Jahren 0
@LewisKelsey: Auf jeden Fall ja, die Vordecodierung muss das * Ende * einer Anweisung finden, bevor sie an die Decoder gesendet werden kann. Die Abschnitte von Agner Fog, die IFETCH-Blöcke erwähnen, sind die Abschnitte vor Core2. Er sagt, Core2 fügte eine Warteschlange zwischen Verzweigungsvorhersage und Befehlsabruf hinzu. Er sagt jedoch immer noch "* Jede Anweisung, die eine 16-Byte-Grenze überschreitet, bleibt übrig, bis der nächste 16-Byte-Block verarbeitet wird. *" Die Vordecodierung basiert also immer noch auf ausgerichteten Blöcken. (Aber Decodieren ist nicht einmal in früheren CPUs enthalten.) Interessante Tatsache: Vor dem SnB konnten die Decoder bis zu 7 Ups (4-1-1-1) ausführen. SnB = 4. Peter Cordes vor 5 Jahren 0
Dank ist eine weitere nützliche Ressource: https://www.intel.co.uk/content/dam/www/public/us/de/documents/manuals/64-ia-32-architectures-optimization-manual.pdf Lewis Kelsey vor 5 Jahren 0