Intel® IA-64 am Beispiel des Intel® Itanium™

von Christian Theiß

Christian.Theiss@mni.fh-giessen.de

Kleines Seminar - 01.10.2001


Itanium Logo



Inhaltsverzeichnis

1 Was ist IA-64
1.1 Vorteile von IA-64 gegenüber IA-32
1.2 Register des IA-64 Prozessors
1.2.1 Register Renaming & Rotation
1.2.2 Register Framing
1.2.3 Register Save Engine
1.3 Kodierung der Instruktionen
1.3.1 Bundles in IA-64 Assembler
1.3.2 Instruktionsblöcke
1.3.3 Prädikate
2 Optimierungen
2.1 Control Speculation
2.2 Data Speculation
2.3 Zählende Schleifen
2.4 Software Pipelining
3 Kompatibilität
3.1 IA-32 Modus
4 Performance
5 Zukünftige Entwicklungen
Literaturverzeichnis

Quellcode-Verzeichnis

Beispiel 1.3.1.a: Ein Bundle in IA-64 Assembler
Beispiel 1.3.2.a: Ein Stück C-Code
Beispiel 1.3.2.b: Ein Stück C-Code übersetzt in IA-32 Assembler
Beispiel 1.3.2.c: Ein Stück C-Code übersetzt in IA-64 Assembler (3 Instruktionsblöcke)
Beispiel 1.3.3.a: Beispiel Prädikate - C-Code
Beispiel 1.3.3.b Beispiel Prädikate - Traditionelle Architektur
Beispiel 1.3.3.c Beispiel Prädikate - IA-64
Beispiel 2.1.a: Beispielcode zur Demonstration der Control Speculation
Beispiel 2.1.b: Beispielcode zur Demonstration der Control Speculation in IA-32
Beispiel 2.1.c: Beispielcode zur Demonstration der Control Speculation in IA-64


Tabellen-Verzeichnis

Tabelle 1.3.a: Aufbau eines VLIW
Tabelle 1.3.b: Die Recheneinheiten für Templates
Tabelle 1.3.c: Aufbau einer Operation


Bilder-Verzeichnis

Bild 1.a: Das Intel® Itanium™ Prozessormodul
Bild 1.b: Ein Intel® Itanium™ Komplettsystem




1 Was ist IA-64?

IA-64 steht für Intel-Architektur mit 64 Bit.

IA-64 ist die Nachfolgearchitektur von IA-32, der Architektur der Pentium Prozessor-Reihe und den dazu kompatiblen Prozessoren wie z.B. AMD Athlon.

Durch Erweiterungen und Optimierungen wurde IA-32 immer komplizierter. Ausserdem ist diese alte Architektur voraussichtlich nur bis ca. 10 GHz skalierbar. Aus diesen Gründen begannen Intel® und HP 1994 mit der Entwicklung einer völlig neuen Architektur, die möglichst gut skalierbar sein sollte und preiswert herzustellen sein sollte.

Itanium Prozessormodul
Bild 1.a: Das Intel® Itanium™ Prozessormodul

Itanium Komplettsystem
Bild 1.b: Ein Intel® Itanium™ Komplettsystem (an den Kühlkörpern kann man in etwa den Stromverbrauch erkennen)

Inhalt

1.1 Vorteile von IA-64 gegenüber IA-32

Zu den Vorteile dieser neuen Technologie zählen der erweiterte Adressraum mit 264 bytes und die Möglichkeit mehrere Instruktionen explizit parallel ablaufen zu lassen durch Speculation, Prediction und Software Pipelining, die im Kapitel Optimierungen näher erläutert werden. Diese Parallelisierung erfolgt durch den Compiler. Das hat den Vorteil, dass ihm fast unbegrenzte Ressourcen zur Verfügung stehen um den optimalen Programmablauf zu bestimmen und dass man Schaltkreise auf der Chipfläche einspart, die diese Aufgabe sonst übernehmen müssten.

Ein IA-64 Prozessor hat sehr viele Register (über 265 Stück), die durch Register Renaming & Rotation sehr effizient genutzt werden können.

Durch die immer gleichbleibende Befehlslänge (siehe Kodierung der Instruktionen) kann die Befehlsdekodierung wesentlich vereinfacht und somit ebenfalls Schaltkreise einspart werden.

Ausserdem werden nun Integerdatentypen mit 64 Bit Breite und Fliesskommazahlen mit 82 Bit genauigkeit unterstützt.

Die ersten IA-64 Prozessoren werden noch abwärtskompatibel zur alten Architektur (IA-32) sein. Dies wird durch eine Hardware-Emulation erreicht. Allerdings ist diese Emulation nicht besonders schnell. Der Itanium™ mit 800 MHz erreicht im IA-32 Modus gerade mal die Geschwindigkeit eines Pentium II mit 300 MHz.

Inhalt

1.2 Register des IA-64 Prozessors

Ein Prozessor der IA64 Architektur enthält:

Inhalt

1.2.1 Register Renaming & Rotation

Die Registerumbenennung ist bei IA-64 Aufgabe des Compilers. Dies spart wieder einiges an Hardware ein. Der Compiler soll hier bessere Ergebnisse erzielen als die Prozessorhardware es könnte. Durch die vielen Register ist der eingesparte Platz allerdings wieder zunichte gemacht. Um auch bei Schleifen viele Register sinnvoll verwalten zu können gibt es die Möglich mit relativen Registeradressen auf die Register zuzugreifen. Die ersten 32 Integer- und Fliesskommaregister sind fix und sozusagen für globale Daten reserviert. Die restlichen 96 Register können rotiert werden. Auch die Prädikat Register können rotiert werden. Das Rotation Register Base Register zeigt auf das erste zu rotierende Register. Da es nur ein Rotation Register Base Register gibt, kann immer nur eine Schleife gleichzeitig davon profitieren. Dadurch ist IA-64 im Gegensatz zu Architekturen mit renaming Hardware etwas eingeschränkt.

Inhalt

1.2.2 Register Framing

Viele Register haben aber auch den Nachteil, dass bei einem Kontextwechwsel oder einem Funktionsaufruf alle Register in den Speicher geschrieben werden müssten. Dies würde bei IA-64 etwa viermal so lange dauern wie bei anderen RISC CPUs. Deswegen gibt es bei IA-64 das sog. Register Framing. Dabei können von Funktionen Register für lokale Variablen mit ALLOC angefordert werden. Dabei können Register als lokale oder als Ausgaberegister angefordert werden. Die Ausgaberegister einer Funktion sind gleichzeitig die Eingangsregister einer von dieser Funktion aufgerufenen Funktion. Es ist somit möglich Daten zwischen Funktionen auszutauschen ohne diese zu kopieren. Die lokalen Register beginnen immer bei Register Nummer 32. Dies ist möglich über den Register Frame Pointer, der immer auf das entsprechende Physikalische Register zeigt. Allerdings müssen dafür der Register Frame Pointer und die Rotations Register Basis auf die relative Adresse aufaddiert werden. Dieser Aufwand ist aber nicht so gross wie der eines kompletten Register renamings.

Inhalt

1.2.3 Register Save Engine

Register Framing bietet die Möglichkeit ohne Register in den Speicher schreiben zu müssen Unterprogrammaufrufe durchzuführen. Insgesamt stehen jedoch nur 128 Register zur Verfügung. Durch Register Rotation wird dieses Problem noch verstärkt. Wenn beim Register Framing mal der Platzt eng werden sollte lagert die Register Save Engine Register ab der Nummer 32 in den Speicher aus bis genügend Register für eine Anforderung verfügbar sind. Dieser Mechanismus läuft für die Software völlig Transparent ab. Wenn wieder auf ausgelagerte Register zugegriffen werden soll schreibt die RSE den Inhalt aus dem Speicher zurück.

Inhalt

1.3 Kodierung der Instruktionen

Instruktion 2 Instruktion 1 Instruktion 0 Template
41 Bits 41 Bits 41 Bits 5 Bits
Tabelle 1.3.a: Aufbau eines VLIW

Instr. Typ Beschreibung ALU
A Integer ALU I oder M
I reine Integer I
M Speicheroperation M
F Fliesskomma F
B Branch B
L, X Extended I oder B
Tabelle 1.3.b: Die Recheneinheiten für Templates

Bei der IA-64 werden immer 3 Instruktionen zu einem Bundle zusammengefasst. Man nennt das VLIW – Very Large Instruction Word. Jeder dieser Befehl hat 41 bits. Zusätzlich gibt es ein Template mit 5 bits, welches angibt welche Recheneinheiten für diese 3 Befehle benötigt werden (z.B. MFI - Memory-, FLiesskomma- und Integereinheit werden benötigt). Jedes bundle hat also genau 128 bits.

Befehle können nicht beliebig gruppiert werden. Der Intel® Itanium™ hat z.B. nur 2 Integer Einheiten. Es muss darauf geachtet werden, dass nicht zu viele gleichartige Befehle in das selbe oder in aufeinanderfolgende Wörter gepackt werden.

Operator Register 1 Register 2 Register 3 Prädikat
14 Bits 7 Bits 7 Bits 7 Bits 6 Bits
Tabelle 1.3.c: Aufbau einer Operation

Ein Befehl setzt sich aus 41 bits zusammen. Es stehen 14 Bits für die Operation zur verfügung. Es kann also 16.384 Befehle geben (ziemlich viel für einen Reduced Instruction Set Computer). Jedem Befehl werden 3 Register zugeordnet mit jeweils 7 bits. Das Prädikat verweisst auf ein Prädikatregister. Enthält das den Wert 0 wird der Befehl nicht ausgeführt.

Inhalt

1.3.1 Bundles in IA-64 Assembler

{ .mfi
  ld r14 = [r56]
  fadd f10 = f12, f13
  add r16 = r18, r19
}

Beispiel 1.3.1.a: Ein Bundle in IA-64 Assembler

An dem Beispiel oben kann man erkennen, wie Bundles explizit in Assembler erzeugt werden können.

Ein Bundle wird in geschweiften Klammern gruppiert. .mfi gibt an, dass ein Memoryzugriff, eine Fliesskomma- und eine Integeroperation stattfinden. Wenn die geschweiften Klammern und der Templatestring weggelassen werden versucht dr Compiler selbst eine möglichst optimale Gruppierung zu finden.

Inhalt

1.3.2 Instruktionsblöcke

If ((a==0) || (b<=5) || (c!=d) || (f % 0x2))
{
  r3=8;
}

Beispiel 1.3.2.a: Ein Stück C-Code

cmp 0,a
je eins
cmp 5,b
jle eins
cmp c,d
jne eins
and 2,f
jnz eins
jmp zwei
eins:
mov 8,r3
zwei:

Beispiel 1.3.2.b: Ein Stück C-Code übersetzt in IA-32 Assembler

cmp.ne p1= r0, r0
add t = -5, b;;

cmp.eq.or p1 = 0, a
cmp.le.or p1 = 0, t
cmp.ne.or p1= c, d
tbit.or p1 = 1, f, 1;;

(p1) mov r3 = 8h

Beispiel 1.3.2.c: Ein Stück C-Code übersetzt in IA-64 Assembler (3 Instruktionsblöcke)

Um mehrere Befehle gleichzeitig ausführen zu können muss sichergestellt sein, dass sie nicht voneinander anhängig sind. D.h., dass keine Daten in einem Befehl verändert werden, die ein einem anderen Befehl benutzt werden. In Assembler werden solche Blöcke, in denen nur voneinander unabhängige Operationen vorkommen durch 2 Semikolons getrennt. Ein Block könnte theoretisch in einem Taktzyklus ausgeführt werden wenn genügend Recheneinheiten zur Verfügung stehen. Beim Itanium™ können max. 2 bundles mit jeweils 3 Befehlen in einem Taktzyklus ausgeführt werden.

Der Vorteil der Einteilung in Blöcke durch den Compiler ist, dass die CPU direkt erkennen kann welche Instruktionen paralell ausgeführt werden können ohne spezielle Hardware zu benötigen. Die Einteilung der Instruktionen in Instruktionsblöcke durch den Compiler nennt sich EPICExplicit Parallel Instruction Computing. Bei IA-32 waren alle Befehle sequetiell angeordnet und die CPU musste „raten“ welche Befehle gleichzeitig ausgeführt werden können.

Inhalt

1.3.3 Prädikate

Beim Inte® Itanium™ gibt es 64 Prädikatregister, die Branches bei bedingtem Code ersetzen sollen. Branch Prediction bei bedingtem Code ist oft nicht sonderlich effektiv und die Hardware dafür benötigt Platz auf der Die.

Mit Hilfe der Prädikate sind if-statements bis zu einer Tiefe von 6 Ebenen möglich ohne auch nur auf einen Sprung angewiesen zu sein. Bedingte Ausführung mithilfe von Prädikaten kostet keine Ausführungszeit, da die CPU sie bei jedem Befehl implizit benutzt.

Das folgende Beispiel verdeutlicht dies. Abhängig davon, ob a[i].ptr != 0 ist, wird entweder a[i].l oder a[i].r nach b[i] kopiert.

If (a[i].ptr != 0)
  b[i] = a[i].l;
else
b[i] = a[i].r;
i = i + 1;

Beispiel 1.3.3.a: Beispiel Prädikate - C-Code

# IA-32
#
#if
load a[i].ptr
p1,p2 = cmp a[i].ptr != 0
jump if p2
#then
load r8 = a[i].l
store b[i] = r8
jump
#else
load r9 = a[i].r
store b[i] = r9

i = i + 1

Beispiel 1.3.3.b Beispiel Prädikate - Traditionelle Architektur

# IA-64
load a[i].ptr
p1,p2 = cmp a[i].ptr != 0

(p1) load r1 = a[i].l
(p2) load r1 = a[i].r

store b[i] = r1

i = i + 1

Beispiel 1.3.3.c Beispiel Prädikate - IA-64

Im Beispiel 1.3.3.b kann man erkennen, wie diese Befehle für eine Traditionelle Architektur übersetzt werden würde. Zuerst muss a[i].ptr geladen werden. Danach wird der Vergleich durchgeführt und zu den entsprechenden Codestellen gesprungen.

Die IA 64, siehe Beispiel 1.3.3.c ,erzeugt beim Vergleich zwei Prädikate p1 und p2. Ist das erste wahr, bedeutet dies, dass a[i].ptr != 0 ist. Das zweite bezeichnet das Gegenteil. Damit ist auch klar, dass nur eines wahr sein kann. Das Laden wird nun parallel für beide möglichen Fälle und abhängig von p1 und p2 ausgeführt.

Je nach Wert von p1 und p2 wird jedoch das Ergebnis einer Seite verworfen, somit stört es auch nicht, dass beide Ladebefehle in das gleich Register schreiben, da ja nur einer ausgeführt wird.

Inhalt

2 Optimierungen

Inhalt

2.1 Control Speculation

Eine Möglichkeit zur Optimierung des Programmablaufs bei der IA-64 bietet die sogenannte Control Speculation. Ladebefehle haben oft lange Wartezeiten, deshalb ist es günstig diese im Programmcode möglichst weit nach vorne zu stellen. Normalerweise hat man das Problem, dass man einen Ladebefehl nicht vor einen Branch ziehen kann. Die IA 64 hat die Möglich solche Instruktionen auch vor einem Sprung auszuführen. Dazu gibt es zu jedem Integerregister ein weiteres Bit, das NaT (Not a Thing) genannt wird. Dieses Bit wird gesetzt wenn zwischen Ladebefehl und dem Verwendungsort etwas fehlgeschlagen ist, z.B. doch ein Sprung stattgefunden hat. An dem eigentlichen Verwendungsort wird dann nur noch dieses Bit überprüft. Und wenn es gesetzt ist der Wert neu aus dem Speicher geladen. Ansonsten wird nur ein NOP ausgeführt. Bei den Floating Point Registern ist dieses NaT durch einen speziellen im IEEE-754 Standard nicht benutzten Wert gekennzeichnet. Man hat den Vorteil, dass man langsame Ladeoperationen früher beginne kann, damit sie bei Verwendung bereits im Register sind.

Betrachten wir folgenden Beispiel-Code:

t1 = t1 + 1;
if (t1 > t2) {
  j = a[t1 - t2];
  b[j]++;
}

Beispiel 2.1.a: Beispielcode zur Demonstration der Control Speculation

#IA-32
add t1 + 1
comp t1 > t2
jump
#hiervor kann nicht geladen werden
load a[t1 - t2]
load b[j]
add b[j] + 1

Beispiel 2.1.b: Beispielcode zur Demonstration der Control Speculation in IA-32

#IA-64
add t1 + 1
ld.s r8=a[t1 - t2]
comp t1 > t2
jump
check.s r8
load b[j]
add b[j] + 1

Beispiel 2.1.c: Beispielcode zur Demonstration der Control Speculation in IA-64

Nehmen wir an, dass Array a so gross ist, dass es nicht in den Cache passt. Um Speicherlatenzzeiten zu eliminieren, ist es notwendig, Arrayelemente so früh wie möglich zu laden. Nun ist es jedoch möglich, dass t1 - t2 einen negativen Wert ergibt und somit eine nicht gültige Adresse. Deshalb kann wie man in Beispiel 2.1.b sieht load a[t1 - t2] nicht vor comp t1 > t2 durchgeführt werden.

In Beispiel 2.1.c sieht man wie das Problem bei der IA 64 gelöst wird. Mit ld.s r8 = a[t1 -t2] wird spekulativ geladen. Geht dabei etwas schief, wird r8 als ungültig markiert und eine exception erzeugt. Nach dem Vergleich wird mittels check.s r8 die exception abgeholt und zum fixupcode verzweigt. Wäre r8 gültig, würde check.s r8 ein NOP verursachen.

Mit spekulativ geladenen Daten können auch Berechnungen durchgeführt werden. NaTs werden dabei mitgeführt. Der fixupcode muss allerdings in der Lage sein alle Auswirkungen dieser Berechnungen rückgängig zu machen. Da laut Schätzungen rund 50 Prozent der Instruktionen spekulativ ausgeführt werden, wird auch sehr viel fixupcode benötigt. Dieser wird zwar meist nicht ausgeführt, er verlängert jedoch das Programm.

Inhalt

2.2 Data Speculation

Die IA 64 bietet auch die Möglichkeit Lesebefehle vor Speicherbefehle zu stellen, auch wenn diese möglicherweise auf die selbe Adresse zugreifen. Die Adresse eines derart vorgezogenen Ladebefehls ld.a wird dazu in den ALAT (advanced load adress table) genannten Buffer eingetragen. Die Adressen von Speicherbefehlen werden im ALAT nachgesehen. Wird eine Adresse gefunden, wird diese gelöscht. Um ein ld.a abzuschliessen, muss, bevor die Daten benutzt werden, ein ld.c ausgeführt werden. Findet ld.c die Adresse im ALAT, wird ein NOP ausgeführt, ansonsten wird der Ladebefehl wiederholt, wobei nun abgewartet werden muss, bis dieser abgeschlossen ist.

Inhalt

2.3 Zählende Schleifen

Eine weitere Möglichkeit zur Optimierung bietet der Loop Counter. Da die Branch Prediction bei Schleifen mit normalen Integer Registern als Schleifenvariablen meist am letzten Befehl fehlschlägt gibt es ein spezielles Loopcount-Register und den Befehl Close-Loop.

Das LC-Register wird mit der Anzahl der Schleifendurchläufe initialisiert. Der Befehl CLOOP am Ende der Schleife verkleinert LC um 1 und springt falls LC nicht null ist zum Schleifenanfang. Falls LC null ist wird nicht mehr gebrancht. Die Hardware kann somit vorzeitig feststellen, ob gesprungen wird oder nicht. Da es nur ein LC-Register gibt kann man diese Technik nur für die innerste Schleife verwenden.

Inhalt

2.4 Software Pipelining

Eine weitere Möglichkeit zur Optimierung ist das Software Pipelining. Beim Software-Pipelining werden mehrere Iterationen einer Schleife direkt nacheinander gestartet ohne auf die Beendigung des vorhergehenden Durchlaufs zu warten. Dazu werden bei jedem Durchlauf die Register rotiert. Diese Technik nennt man Modulo Scheduling. Es gibt ein zusätzliches Register. EC- Epilogue Count welches angibt, wieviele Durchläufe noch beendet werden müssen, nachdem der LoopCount bereits 0 ist.

Inhalt

3 Kompatibilität

Inhalt

3.1 IA-32 Modus

Für IA-64 Prozessoren ist es möglich in den IA-32 Modus geschaltet zu werden. In diesem Kompatibilitätsmodus ist es möglich alle Befehle eines IA-32 Prozessors auszuführen. Auch MMX und SSE Instruktionen sind verfügbar. Für Integer und FP-Register werden einige Register des IA-64 direkt verwendet. Für die MMX und SSE Befehle werden je 2 FP Register benutzt (MMX und SSE Register haben 128 Bits). Die Ausgührgeschwindigkeit ist im IA-32 Modus sehr langsam, da z.B. die Renaming Hardware fehlt und emuliert werden muss.

Mischbetrieb zwischen IA-32 und IA-64 Code ist äusserst ungünstig, da ein Moduswechsel sehr lange braucht und alle Register gesichert werden müssen.

Inhalt

4 Performance

Mit einem SPECint2000-Wert von 345 (base) liegt er unter den Erwartungen etwa auf Pentium-III-800-Niveau, und damit weit hinter Pentium 4-2GHz, Athlon 1,4GHz, UltraSPARC-III-900, Alpha 21264B-500 oder HP PA8700-750.

Im Gleitkommabereich jedoch hält die neue Architektur jedoch, was sie versprach. Mit einem SPECfp2000-Basiswert von 700 übernimmt er die Spitzenposition klar vor dem Pentium 4, der bei 1,7 GHz einen SPECfp2000 von 595 erzielt. Suns UltraSPARC-III-900 erzielt hier nur 427 Punkte.

Ähnlich gut sieht es mit den Supercomputer-Benchmarks Linpack-1000 und Cactus für den Itanium™ aus. Nur die gigantischen und enorm teuren Vektorprozessoren von Fujitsu und NEC sind hier schneller. Doch mit einem Preis zwischen 1127 (733 MHz, 2MByte L3) und 4227 Dollar (800 MHz, 4MByte L3) ist er für einen Serverprozessor sehr günstig.

Inhalt

5 Zukünftige Entwicklungen

Nachdem mit dem Itanium™ der erste 64-Bit Prozessor von Intel® auf dem Markt ist, soll demnächst schon sein Nachfolger der McKinley kommen und mit mindestens 1 GHz laufen. McKinley wird kaum noch 32 Bit-Code unterstützen. Bereits für 2003 ist ein McKinley Prozessor mit 3 GHz geplant.

Inhalt



Literaturverzeichnis