Details und Analyse der Zen-Architektur nach der Hot-Chips-Konferenz

Artikel-Index:

Die Decoder-Einheit im Detail

In der Decoder-Einheit landen die 32 aus dem Befehlscache gelieferten Bytes mit Programmcode im Instruction Byte Buffer. Aus diesem selektiert ein Picker vier identifizierte x86-Befehle und leitet sie an die Decoder-Einheit, welche diese dann in ein internes Befehlsformat (MOp) übersetzt und an einen MOp-Puffer schickt. Im Falle eines Treffers im MOp-Cache können von diesem sogar bis zu 6 MOps in einem Taktzyklus an den Puffer geliefert werden, was auf Skylake-Niveau liegt. Beim Weg über die Decoder liegt der Intel-Prozessor dagegen mit bis zu 5 Befehlen pro Takt leicht vorn. Nicht gezeigt ist der Weg der decodierten Befehle von der Decoder-Einheit in den MOp-Cache. Falls im Programmcode ein nicht korrekt vorhergesagter Sprung stattfindet, verkürzt sich dank des Caches auch die sogenannte Branch Misprediction Penalty, also die Dauer bis zur Ausführung der korrekten angesprungenen Befehle. Der Cache spart hier mehrere Taktzyklen, was die Leistung steigert.

Aus dem MOp-Puffer (µOp-Queue)) heraus wird für komplexere, nicht direkt in Hardware implementierte Befehle die Ausgabe der dafür notwendigen Microcode-Befehle aus dem Microcode-Speicher (ROM) angestoßen. Zudem untersucht eine Stack Engine den Code im Puffer auf Stack-Befehle, welche üblicherweise für die Zwischenspeicherung von Werten oder Übergabe von Parametern verwendet werden. Sind solche vorhanden, kann die Engine die Adressen dank typischerweise einfacher Offset-Adressierung (z.B. RSP+8) schon hier berechnen. Das gab es auch schon in früheren Kernarchitekturen, wie z.B. der Bulldozer-Linie. Da diente es aber nur dem Zweck, die Abhängigkeiten zwischen Stack-Befehlen aufzulösen, um diese parallel ausführen zu können. Die eigentlichen Stack-Speicherzugriffe mussten weiterhin komplett ausgeführt werden, was Ressourcen belegte und Energie verbrauchte.

Ein neuer Ansatz hier ist nun, sich die Adressen von Stack-Schreibzugriffen (z.B. PUSH RAX) zu merken und diese Daten an später auf die gleiche Adressen zugreifenden Lesebefehlen (z.B. ein darauf folgendes POP RCX) direkt weiterzuleiten. Da nicht weiter auf das Memfile eingegangen wurde, gehen wir spekulativ davon aus, dass es wie ein kleiner Stack Cache funktioniert. Es könnte dafür die Immediate-/Displacement-Einheit verwenden, welche hier nicht explizit gezeigt wurde, obgleich schon in der Bulldozer-Reihe präsent. Dort legt das Front-End die bei x86-Befehlen mit einem Konstantenwert mitgelieferten Werte ab und stellt diese bei Befehlsausführung ggf. bereit. Für diese Zugriffe kann also auf die Nutzung einer Address Generation Unit (AGU) und der Load-/Store-Einheit sowie des Level-1-Datencaches verzichtet und damit Energie gespart werden. Freilich muss die Engine noch überwachen, ob irgendwelche anderen Speicherzugriffe sich mit den Stack-Zugriffen überlappen, um Konflikte zu verhindern und immer die korrekten Daten zu verwenden.

Nach all diesen Vorarbeiten können die verarbeiteten Befehle an den Dispatch-Puffer gesendet und dort vorgehalten werden. In diesem Puffer werden die Befehle von der kompakteren MOp-Form in einzelne µOps für die verschiedenen Funktionseinheiten aufgesplittet. Hierbei können in einem Takt bis zu 6 µOps an die Integer-Einheit sowie 4 µOps an die Fließkomma-Einheit gesendet werden. Wie in der Fragerunde zu vernehmen war, werden zum Beispiel komplexere Operationen wie 256 bit breite AVX(2)-Befehle erst hier von einer MOp in 2 µOps aufgeteilt. Dies ist nötig, da AMDs FPU nur halb so breit ist. Genaueres folgt dazu in der FPU-Beschreibung. Das spart Bandbreite für MOps im Front End, welche dann für andere Befehle zur Verfügung steht. Somit können mehr Befehle aus dem Decoder-Front-End in die Rechenwerke des Back-Ends fließen. Intels Marketingabteilung würde dieses Feature vermutlich AVX-Fusion o.ä. nennen 😉

« Die Fetch-Einheit im Detail» Die Integer-Einheit im Detail