Registerumbenennung

Registerumbenennung (englisch register renaming) bezeichnet eine Phase in der Befehlsdekodierung, die meist von superskalaren Mikroprozessoren angewandt wird. Sie hilft, unnötige Sequentialisierung zu vermeiden bzw. verbessert die Out-of-Order-Execution, d. h. die Möglichkeit, kleine Teile des Programms nebenläufig auszuführen.

Motivation

Superskalare Prozessoren besitzen einen verhältnismäßig kleinen Satz an direkt ansprechbaren Architekturregistern, die durch den Befehlssatz vorgegeben sind. Es können daher nicht mit jeder Prozessorgeneration neue Register im Befehlssatz aufgenommen werden, weil das die Binärkompatibilität brechen würde. Deshalb können aber im Programmablauf Datenabhängigkeiten zwischen Programmteilen auftreten, die eine Out-of-Order- bzw. nebenläufige Ausführung verhindern. Es handelt sich dabei allerdings nicht um echte Abhängigkeiten, sondern um Namensabhängigkeiten, in diesem Fall auch Datenkonflikte genannt.

In der x86-Familie kam Registerumbenennung erstmals im Pentium Pro zum Einsatz, in dem acht für den Programmierer sichtbare bzw. ansprechbare Register des Befehlscodes auf 96 unsichtbare bzw. nicht ansprechbare aber physisch vorhandene Register umgelegt wurden.

Die Abhängigkeiten Write After Read (WAR) und Write After Write (WAW) können nun durch Registerumbenennung aufgelöst werden. Dazu existiert eine große Zahl an Schattenregistern, die nicht direkt vom Programm verwendet werden können. Üblicherweise wird nun in der Dekodierstufe (ID) des Prozessors bei jeder Definition eines Registers, sprich bei jeder Instruktion, die ein Ergebnis produziert und es in einem Register ablegen möchte, das verwendete Register in ein Schattenregister umbenannt – daher der Name. Danach enthält der Code nur noch echte Datenabhängigkeiten und die unabhängigen Teile können parallel oder in einer anderen Reihenfolge ausgeführt werden.

Beispiel

Der folgende Code kann in dieser Form nur sequentiell abgearbeitet werden, da Datenabhängigkeiten bestehen.

1:  R1 := R2 / R3
2:  R4 := R1 + R5   # RAW-Abhängigkeit mit Zeile 1
3:  R5 := R6 + R7   # WAR-Abhängigkeit mit Zeile 2
4:  R1 := R8 + R9   # WAW-Abhängigkeit mit Zeile 1

Benennt man nun konsequent das Zielregister jeder definierenden Operation um, lösen sich die WAR- und WAW-Abhängigkeiten auf:

1:  A := R2 / R3
2:  B := A  + R5   # RAW-Abhängigkeit mit Zeile 1
3:  C := R6 + R7
4:  D := R8 + R9

Die Blöcke (1 und 2), 3 und 4 können nun in beliebiger Reihenfolge oder auch parallel ausgeführt werden.

Alternativen

Einen anderen Ansatz ist man mit dem Explicitly Parallel Instruction Computing beim Itanium-Prozessor gegangen, dieser stellte sich jedoch als weniger erfolgreich heraus. Dabei werden Anweisungen, die parallel ausgeführt werden können, in speziellen Instruktionen kodiert und zu sog. Anweisungsblöcken (instruction groups) zusammengefasst. Ein Nachteil dieser Methode ist, dass der Zielprozessor zum Zeitpunkt der Übersetzung bekannt sein muss und eine nachträgliche Anpassung nicht mehr bzw. nur noch durch erneutes Übersetzen möglich ist.

Siehe auch

Literatur

  • John L. Hennessy, David A. Patterson: Computer Architecture: A Quantitative Approach, S. 208 ff., Elsevier, 2012, ISBN 9780123838728.
  • Christian Müller-Schloer, Wolfgang Karl, Sami Yehia: Architecture of Computing Systems – ARCS 2010: 23rd International Conference, Hannover, Germany, February 22-25, 2010, Proceedings, S. 127 ff., Springer Science & Business Media, 2010, ISBN 9783642119491.
  • Jean-Loup Baer: Microprocessor Architecture: From Simple Pipelines to Chip Multiprocessors, S. 89 ff., Cambridge University Press, 2010, ISBN 9780521769921.