Compiler-Techniken für superskalare Prozessoren

Superblöcke

Zwei Ansätze zur Vergrößerung dieser Basisblöcke werden im Folgenden diskutiert. Der erste Ansatz versucht, die Größe des wesentlichen Basisblocks zu erweitern. Das Zusammenfassen eines Zweiges zu einem Superblock, der innerhalb der Ausführung möglichst häufig durchlaufen wird, soll die Performance verbessern. Voraussetzung dafür ist, dass der andere Zweig eine seltene Ausnahme ist.

In dem Beispiel sei nun angenommen, dass der Prozessor den ELSE-Zweig nur selten durchläuft. Dann variiert man den Assembler-Code und den Kontrollflussgraphen so, dass man den Block L4 kopiert und aus den Blöcken L1, L2 und L4 einen Superblock (punktiert eingerahmt) erzeugt.

Die eigentliche Optimierung für den Assembler-Code besteht nun darin, dass die Ladeoperationen parallel zueinander ausgeführt werden, wodurch die CPU zwei Takte spart. Diese Ladeoperationen erfolgen für den ELSE-Teil unnötig. In diesem Beispiel führt dies aber zu keiner Verschlechterung der Geschwindigkeit, der ELSE-Zweig benötigt weiterhin drei Takte. Das Beispiel berücksichtigt jedoch gemäß den Voraussetzungen keine Verzögerungen durch falsche Sprungvorhersagen. Dies gilt im Allgemeinen jedoch nicht, teilweise erfolgt die Optimierung der Hauptschleife auf Kosten des Sonderfalls.

Das Resultat dieser Bemühungen ist eine Verbesserung der Performance auf 2,0 Instruktionen pro Takt und ein Zeitverbrauch von nur noch vier Takten pro Schleifendurchlauf. Diese als Global Acyclic Scheduling bezeichnete Beschleunigung beruht im Beispiel vor allem auf der parallelen Ausführung der Load-Befehle. Das Attribut global entstammt dabei dem Vorgehen, über die bisherigen Basisblockgrenzen hinweg Befehle zu verschieben, während die Bezeichnung azyklisch für die Begrenzung innerhalb des azyklischen Teils stammt. Die Verschiebung der Load-Befehle aus dem Bereich nach dem Branch-Befehl in den Block davor, der ja in jedem Fall eine garantierte Ausführung bewirkt, bezeichnet man als Speculative Code Motion.

Die Verschiebungen sind zulässig, wenn die Ausführung des ELSE-Teils dadurch keine falschen Resultate hervorruft. Dies muss der Compiler zusätzlich prüfen, im Beispiel führt es nicht zu Problemen. Zusammenfassend gilt, dass das Einfügen von Superblöcken den größten Gewinn erzielt, wenn ein Pfad des Codes besonders häufig durchlaufen wird. Bei mehreren Pfaden kann dieses Verfahren jedoch eher das Gegenteil bewirken, da die Optimierungen dann eventuell an falscher Stelle erfolgen.