Extralange Befehlsworte sorgen für hohe Rechenleistung:

Effizienz ohne Vektorisierung durch den VLIW-Ansatz

12.02.1988

In der an Innovationen gewiß nicht gerade armen Welt der Computertechnik eine Maschine zu präsentieren, die weit über den Tag ihrer Erstvorstellung hinaus das Interesse der Fachwelt auf sich zieht - das ist eine beachtliche Leistung. Deshalb ist es wohl gerechtfertigt die neuen "Trace"-Maschinen des US-Herstellers Multiflow einmal näher unter die Lupe zu nehmen; Maschinen, für die sich in Deutschland die Aachener Firma GEI stark macht.

Bei den Trace-Rechnern handelt es sich, ihrer Zweckbestimmung nach um eine Art "Minisupercomputer", ähnlich den bekannten Rechnern von Firmen wie Convex oder Alliant; doch verzichtete Multiflow auf die sonst übliche spezielle Schaltung zum Abarbeiten vektorisierter Code - abschnitte. Dennoch sollen diese Trace-Rechner schon in ihrer kleinsten Konfiguration, nämlich als Modell 7, annähernd so schnell wie ein Alliant-FX-8 mit acht Recheneinheiten oder auch ein IBM-3090-200 mit einer Zentraleinheit sein,- betrachtet man die Leistung der Maschine beim Bearbeiten der bekannten Linpack-Testprogramme. Gegenüber einer Convex-C1-XP beziehungsweise gegenüber einer DEC-VAX-8600 soll die Trace 7 sogar das doppelte beziehungsweise das sechsfache Tempo erreichen. Und zwar vor allem, weil sie mit einem hochinteressanten Compiler arbeitet, mit einem Übersetzer nämlich, der die intern mehrfach vorhandenen Bearbeitungseinheiten der Trace-Maschine besonders gut auszulasten versteht, und der dabei mit einer bemerkenswerten Art von extralangen Instruktionswörtern arbeitet, wie sie gerade für die Trace-Rechner typisch sind.

Hohe Geschwindigkeit nicht nur bei Vektoroperationen

Vergleicht man die Trace-Rechner mit herkömmlichen Vektor-Maschinen, also mit typischen Super- und Minisupercomputern, so fällt auf, daß erstere dank des speziellen Trace-Übersetzers Vektor-Rechnungen fast ebenso schnell wie letztere abarbeiten können. Und dies, obwohl ihnen doch jene teuere spezielle Vektor-Recheneinheit fehlt, die bei Vektor-Rechnern eigens auf jenen streng geordneten Befehls- und Datenstrom zugeschnitten ist, der für die Bearbeitung regelmäßig strukturierter Datenfelder so kennzeichnend ist.

Während aber Vektor-Rechner allein im "vektoriellen" Teil eines Programms ihr bekannt hohes Tempo erreichen, nicht jedoch im herkömmlich wirren, "skalaren" übrigen Code, arbeiten Trace-Maschinen, und zwar wiederum dank ihres Compilers, auch auf diesen Code-Abschnitten ausgesprochen schnell. Und das bedeutet, auf eine kurze Formel gebracht: Im vektoriellen Teil eines Programms sind die Trace-Computer den Vektor-Rechnern wegen der eingesparten Vektor-Recheneinheit überlegen, fragt man nach dem Verhältnis von Preis zu Gegenwert, und im skalaren Teil, weil sie da ganz einfach mehr Tempo als herkömmliche Vektor-, aber auch einfache Rechner vorlegen.

Vergleicht man die Trace-Maschinen hingegen mit Mehrprozessor-Parallelarchitekturen, so sind sie jenen nach Ansicht von Fachleuten unter anderem dadurch überlegen, daß sie mit einem einzigen Befehlsstrom - und mithin nur mit einer einzigen Prozessorsteuerungs-Logik - operieren. Außerdem sei kurz erwähnt, daß die neue Maschine intern auch noch typische Merkmale eines RISC-Rechners mit seiner einfachen billigen und dennoch schnellen Technik - und mit seinen einfachen und flott bearbeitbaren Maschinen befehlen - aufweist. Was gleichfalls wieder Kosten sparen und Tempo bringen soll.

Nimmt man die Trace-Rechner noch näher unter die Lupe, so fällt mit als erstes ihr exotisch langes Befehlswort ins Auge: Schon bei der Basismaschine Trace 7 umfaßt es nämlich bis zu sechs 32-Bit-Befehle sowie zusätzlich einen - RISC-typisch verzögerten - Sprungbefehl. Und ein voll ausgebauter Trace-28-Rechner bringt es gar auf viermal soviel Arbeits- und Sprungbefehle pro Instruktionswort.

Von den 7 bis 28 einzelnen Befehlen des Befehlswortes spricht jeder eine andere funktionelle Einheit im Inneren der Maschine an. Wobei allerdings ohne weiteres auch mehrere Befehle die gleiche Einheit betreffen können; denn da sie einzelnen Bearbeitungs-Einheiten intern wiederum teilweise nach dem Pipeline-Prinzip, also in überlappender Arbeitsweise, tätig sind, ist es eben möglich, daß eine Einheit im selben Maschinen-Zyklus gleich zwei Befehle quasi simultan ausführt.

Trace-Rechner kommen natürlich nur dann auf hohe Geschwindigkeit wenn alle ihre internen Funktionseinheiten möglichst stark ausgelastet sind. Das heißt, der spezielle - Compiler dieser Systeme muß den vorgegebenen Quellcode intensiv auf Befehle durchsuchen, die problemlos gleichzeitig ausgeführt werden können und die er daher, ohne Störungen im Programmlauf zu provozieren, alle in ein und dasselbe extralange Befehlswort packen kann.

Programmsprünge - eine Reise ins Ungewisse?

Diese Aufgabe ist schon dann schwer lösbar, wenn dem Compiler nur ein simpler linear-sequentiell abzuarbeitender Programmabschnitt vorgesetzt wird; denn auch hier lassen sich manche "späteren" Befehle ja erst bearbeiten, wenn - beispielsweise - die dazu nötigen Variablen zunächst von früheren Befehlen errechnet worden sind. Doch noch viel schwerer wird die planerische Aufgabe des Compilers, betrachtet man Abschnitte voller bedingter Verzweigungen. Denn wie soll der "Trace-Scheduling-Compiler" der Multiflow-Maschinen denn vorausahnen, welcher Zweig als nächstes durchlaufen wird? Und welcher mithin auf "neutrale", gleichzeitig früher zu bearbeitenden Befehlen ausführbare Instruktionen durchsucht werden muß? Wo doch, bei bedingten Verzweigungen, allein der Wert den Sprungparameter bestimmt, wohin die Reise fortan gehen soll ...

Alle Eventualitäten werden untersucht

Der neuartige Trace-Compiler kann nun natürlich in der Tat nicht "wissen" oder mit Sicherheit "errechnen", auf welchem Weg eine Gabelung des Programms im konkreten Einzelfall wohl durchlaufen werden mag. Doch er kann oft von gewissen Wahrscheinlichkeiten ausgehen und dann sozusagen mit einem hohen Maß an Sicherheit erwarten, daß eine Verzweigung - etwa am Ende einer Iteration zur schrittweise immer exakteren Berechnung eines Werts - an den Beginn dieser Iterations-Programmschleife zurückführt; denn nur vergleichsweise selten wird ja dort mit dem endgültigen Wert der zu errechnenden Variablen im Programm linear fortgefahren.

Noch mehr an Wahrscheinlichkeitswissen dieser und ähnlicher Art kann der Compiler einsetzen, läßt man ihn zunächst sozusagen eine "Lernphase" durchlaufen. Dabei wird das vorliegende Quellprogramm zusammen mit typischen Daten eingegeben und der Compiler notiert sich dann bei jedem kritischen Punkt des Codes, mit welcher Wahrscheinlichkeit die einzelnen Programmzweige künftig wohl durchlaufen werden dürften. Diese statistischen Angaben kann er später, beim eigentlichen Übersetzen des Quell-Codes in Maschinenbefehle, nutzen - und die Trace-Befehlsworte mit ihren bis zu 28 Instruktionen, entsprechend geschickt mit Einzelbefehlen vollpacken.

Es leuchtet ein, daß allein schon diese Eigenschaften des Trace-Compilers einen gewissen Gewinn an Geschwindigkeit versprechen, vergleicht man ihn mit herkömmlichen - und auf herkömmliche Kurzwort-Maschinen abgestimmten - Übersetzern. Doch damit allein haben seine Entwickler sich noch längst nicht zufriedengegeben.

Weitere Leistungen dieses Compilers werden verständlich, führt man sich zunächst vor Augen, daß er aufgrund seiner statistisch unterlegten Vorausschätzungen, doch eigentlich jeweils sogenannte "Spuren" (Traces) zusammenstellt, also Gruppen von Befehlen, die in die einzelnen, breiten Instruktionswörter gepackt werden - und die auch gewiß immer dann richtig zusammengestellt sind, wenn das Programm den erwarteten Verzweigungsweg auch tatsächlich durchläuft. Doch wenn der wahre Pfad mal vom erwarteten abweicht, sieht es natürlich um so schlechter aus.

Für diese Fälle der - möglichst seltenen - Verzweigung in die nicht erwartete Richtung stellt der Trace-Compiler vorsorglich weitere, alternative "Spuren" zusammen, die nun allerdings interessante Besonderheiten aufweisen. Denn diese sekundären Spuren umfassen neben dem normalen, aus der Logik des Quellprogramms sowieso folgenden Code noch eine Art kompensatorisch wirkenden, zusätzlichen Code, der eine ganz spezielle Funktion vollführt. Er macht nämlich all das wieder ungeschehen, was vom vorangehenden Befehlswort zwar schon bewirkt worden, was aber - angesichts der unerwarteten Verzweigung - nun nicht mehr zulässig ist. Und außerdem gehören zu diesem Kompensations-Code auch Befehle, die Aktionen bewirken, die allein für den jetzt eingeschlagenen Programmzweig wichtig sind. Und die eigentlich bereits hätten ausgeführt werden müssen, doch waren sie unterblieben, da der Rechner doch die Ausführung des anderen Programmzweiges "erwartet" hatte ...

Die Programmpfad-Vorausplanungstechnik, mit der der Trace-Compiler arbeitet, wurde schon in den späten siebziger Jahren vom jetzigen Multiflow-Manager Dr. Joseph Fisher an der Universität New York sowie an der Yale University entwickelt, und zwar ursprünglich im Rahmen von Forschungen, die bemerkenswerterweise der Erarbeitung effizienter Mikrocodes für horizontal mikroprogrammierte Rechner dienten. Also für Maschinen, bei denen ein Maschinenbefehl nicht etwa die sequentielle Bearbeitung einer Kette einzelner Mikrobefehle startet, sondern bei denen er in ein sehr breites, viele Bit umfassendes Mikrobefehlswort übersetzt wird. Und bei denen jenes dann von den Funktionselementen der Maschine auf einen Schlag ausgeführt wird.

Die Zerlegung eines völlig beliebigen Programms in lauter einzelne Befehle, die dann gruppenweise weitgehend zeitgleich abgearbeitet werden, macht die Trace-Rechner zu höchst "feinkörnig" arbeitenden Mehrprozessor-Systemen, betonen Fisher und sein Team. Denn selbst Mehrprozessor-Parallelrechner arbeiten ja mit Code-Blocks von jeweils vielen einzelnen Befehlen; und auch Vektor-Prozessoren sind nur dann wirklich effizient nutzbar, wenn Programmschleifen viele Dutzend Mal durchlaufen werden, ehe der Rechner sie wieder verläßt. Ganz zu schweigen davon, daß skalarer Code Vektor-Rechner extrem schlecht nutzt und viel Zeit benötigt, ehe er bearbeitet ist. Denn mit ihm kann die teure Vektor-Einheit ja absolut nichts anfangen . . .

Die besonderen Fähigkeiten des neuen, die wahrscheinlichen Programmpfade im voraus ermittelnden Trace-Compilers bedingen die Erzeugung von ungewöhnlich viel Maschinencode, denn beispielsweise das Aufdröseln von Programmschleifen zur Erweiterung der Parallelisierungsmöglichkeiten sowie ferner ein gewisser Code-Überhang, der zum Vermeiden von unerwünschten "No - operation" - Instruktionen benötigt wird, blähen den entstehenden Maschinencode auf. Auch muß manchmal Code erzeugt werden, der über mehrere Schritte Funktionen bewirkt, für die die internen RISC-Prozessoren keinen kurzen, mächtigen Einzelbefehl kennen, und schließlich fordert natürlich auch der Kompensationscode sein Patz. Was alles zusammen zur Folge hat, daß manche Objektdatei bei einem Trace-Rechner glatt dreimal so viele Bytes umfaßt wie zum Beispiel bei einer VAX mit ihrem - leistungsfähige Befehle bietenden CISC-Prozessor (CISC = Complex Instruction Set Computer; das Gegenstück von RISC).

Auch Compiler profitiert vom VLIW-Ansatz

Nun mag umfangreicher Maschinencode in Zeiten fallender Preise und steigender Kapazitäten der üblichen Halbleiter- und Plattenspeicher zwar nicht unbedingt ein drückendes Problem sein - doch da wäre noch etwas anderes. Und zwar fragen kritische Beobachter gern, wie es bei der Trace-Technik denn wohl um die Übersetzungszeiten, die der Compiler benötige, stehe, und wie die Maschine sich wohl beim Umschalten von einem Programmteil auf einen anderen samt dem zugehörigen Retten und Neuladen der Registerinhalte benehmen mag.

Was nun erstens die Laufzeit des Übersetzers betrifft so können Fisher und seine Kollegen die Kritiker beruhigen: Denn der Compiler selber profitiere ja auch von der internen Parallelarchitektur der Trace-Rechner - und arbeite daher ähnlich schnell wie andere, hochgradig optimierende Übersetzer.

160 Register sind zu retten

Und das Umschalten von einem Programmkontext in einen anderen bedingt zwar, daß die Inhalte aller Register abgespeichert und alle Fließbänder in den Bearbeitungselementen "geleert" werden, doch soll die Trace-Technik dies in nicht einmal 16 Mikrosekunden schaffen. Diese Verzögerung aber falle im Vergleich zu den übrigen, unvermeidlichen Zeiteinbußen beim Kontextwechsel sowieso nicht ins Gewicht.

Und damit auch Prozeduraufrufe flott abgewickelt werden, plant der Compiler schon während des Übersetzens die Belegung der 160 und mehr Register des Rechners so, daß später möglichst wenig Inhalte abgespeichert werden müssen. Dabei erzeugt er außerdem explizit auch noch jenen Code, der bei Prozeduraufrufen zum "Retten" der Registerinhalte sowie zur späteren Neubelegung der Register benötigt wird.