| Home | Computer | Hardware | Site Map |
Die Ausführung eines Befehles geht nicht in einem Rutsch, sondern es sind mindestens 4 Phasen notwendig, die sich je nach Befehl in weitere Unterphasen aufteilen lassen.
Jeder Prozessor hat eine Anzahl sehr einfacher Befehle die
typisch für ihn sind und nicht auf andere Prozessoren übertragbar. So gibt es nicht eine
Maschinensprache sondern eine für Power PC, eine für Pentium und eine für Alpha Prozessoren.
Verknüpft ist diese Maschinensprache mit der Architektur des Prozessors und diese spiegelt sich auch in den Registern wieder. Stellen wir uns vor, wie würden einen neuen 16 Bit Prozessor bauen der 16 MB (24 Bit) Adressieren kann. So ein Prozessor hat typischerweise um die 60-200 Maschinenbefehle. Nehmen wir mal an es wären 64. Diese könnten wir bequem in einem Byte dekodieren, das ja 256 Zustände einnehmen kann. Doch so einfach ist es nicht. Viele Befehle benötigen einen oder sogar zwei Operanden. Hier ein Beispiel:
Selbst wenn nicht alle Befehle Operanden haben, und man nicht alle Kombinationen zulässt gibt es ohne Probleme bei 3 oder 4 Registern - und dass ist nicht gerade viel - schon mehr Kombinationsmöglichkeiten als in ein Byte hineinpassen. Man benötigt also dann zwei Bytes für einen Befehl. Damit wird zum einen der Code länger, zum anderen dauert das Dekodieren von 2 Byte natürlich länger als von einem Byte.
Es gibt nun zwei Ansätze dieses Dilemma zu lösen. Beim CISC Ansatz (Complex Instruction Set Computer) versucht man mit weniger Registern auszukommen dafür mächtige Befehle zu benutzen und oft spezialisierte Register. Es gibt dies schon bei 8 Bittern wie dem Z80 der mit einem Befehl einen ganzen Speicherblock verschieben kann. Auch der 8086 und seine Nachfolger haben nur wenige freie allgemein nutzbare Register (AX,BX,CX und DX) die spezialisiert sind. Selten gebrauchte Operationen versucht man in 2 Byte zu kodieren, die häufigen dagegen in einem. Die Länge des Codes ist damit variabel und schwankte schon bei einem 8 Bit System zwischen einem und 5 Byte.
Bei RISC sagt man sich dagegen: In Ordnung, ich kann nicht alle Befehle in einem Byte kodieren, dann versuche ich wenigstens die Möglichkeiten mit 2 Byte voll auszunützen indem ich das zusätzliche Byte nutze um erheblich mehr Register anzusprechen. RISC Prozessoren haben daher oft sehr viele Register auf dem Chip - 32,64 oder gar 128 und alle sind gleichberechtigt. Es gibt nur die einfacheren Maschinenbefehle, nicht die Superbefehle von CISC. Den Nachteil von längerem Code versucht man mit einem einheitlichen Format zu begegnen. Bei unserem Papierprozessor z.B. könnte man sagen: Alle Befehle die mit Werten oder Adressen zu tun haben werden in einem Byte kodiert, die nächsten 3 Byte entfallen dann auf die Adresse/Daten und alle anderen Befehle werden in 2 Byte kodiert, dann entfallen die Bytes 3 und 4 auf jeweils die Nummer von einem von 256 Registern. Alle Befehle wären dann 32 Bits lang und man könnte 256 Register nutzen. Dafür verschwendet man Platz, denn bei CISC gibt es variable Befehlslängen, z.B. benötigt nicht jeder Befehle zwei Operanden, so das bei manchen Befehlen das 4 Byte oft unbenutzt wäre.
RISC entstand Mitte der 80 er Jahre als Bewegung aus zahlreichen Universitätsinstituten heraus und wurde von Hardwareherstellern aufgegriffen welche die Chance sahen in der Geschwindigkeit mit den Intel Prozessoren mitzuhalten, ohne über die finanziellen Mittel zur Verfügen um die gleichen Komplexen Prozessoren zu fertigen. Mehr über RISC und CISC in einem eigenen Aufsatz.
Schon immer inspirierten sich beide Konzepte: C entstand als hardwarenahe Programmiersprache und beherrscht eigentlich nur die grundlegenden Datentypen des Prozessors. Andererseits wurde der heute in allen Prozessoren anzutreffende Stack wurde zum ersten mal in den 60 ern von Wirth für seine virtuelle Pascal Maschine eingeführt und von den Prozessoren erst später übernommen. Vor allem bei den Datentypen nähern sich die Konzepte an: Anders als ein Prozessor gibt es bei Hochsprachen z.B. verschiedene Datentypen die ein Prozessor nicht kennt :
Fast alle Prozessoren führten jedoch einen Indexzugriff auf den Speicher ein, meistens mit einem Register als Basisadresse und einem zweiten Register als Offset. Durch erhöhen dieses Offsets in festen Werten konnte man so leicht über ein Array iterieren. Diese Zugriffsmethode ist heute Standard
Auch für Strings gab es eigene Befehle. Die 80x86 Serie kann z.B. mit einigen Befehlen einen String kopieren, eine Subposition eines Zeichen finden oder zwei Strings vergleichen. Bei vielen RISC Prozessoren findet man aber nicht diese Hochspezialisierten Befehle
Funktionsaufrufe in Hochsprachen laufen üblicherweise über den Stack ab: Ein Speicherbereich auf den Register abgelegt werden wobei man immer nur am Ende ein Register sichern (PUSH) oder eines lesen (POP) kann. So werden Argumente an die Funktion auf dem Stack abgelegt und dann von diesem wieder von der Funktion geholt und eventuell Ergebnisse wieder dort abgelegt. Der Stack liegt aber nicht im Prozessor sondern im Hauptspeicher und so ist diese Vorgehensweise sehr zeitaufwendig, weshalb einige Sprachen wie C/C++ mit Makros arbeiten - Codeschnipsel die eingefügt werden und Funktionen ersetzen.
Beim SPARC Prozessor wurde zum ersten Mal das Register Windows eingeführt: Der sehr große Registersatz setzt sich aus immer sichtbaren Registern zusammen und einem Satz von 16 Registern die gewechselt werden können: Beim Aufruf einer Funktion muss nur dieser Kontext gewechselt werden, wobei dies im Prozessor erfolgt und sehr schnell ist. Der Stack ist für Argumente nicht mehr nötig. Diese Technik wurde in Folge auch von anderen Prozessoren übernommen.
Doch schon bei den 16 Bittern war's aus mit diesem einfachen Leben, es kamen die ersten Entwicklungen auf die es erlaubten die Taktfrequenz des Prozessors über dem des Speichers zu setzen. Diese Notwendigkeit ergab sich einfach aus den steigenden Taktfrequenzen. 1978 erschien der 8086 mit 5 MHz, die Taktfrequenz stieg zuerst nur langsam an und erreichte bis 1982 8 MHz beim 80286, doch dann begannen die Taktfrequenzen schnell zu steigen: 1985 16 MHz, 1989 33 MHz, 1993 66 MHz... Eine Verdopplung alle 3 Jahre. (Seither noch schneller, von 1998 bis 2001 stiegt die Taktfrequenz von 500 auf 2000 MHz, also um das 4 fache in 3 Jahren).
Doch Speicherchips konnten nicht so schnell gesteigert werden, bis zum Einführung von SD-RAM 1996 stieg innerhalb von 20 Jahren die Zugriffszeit von Speicher von 250 ns auf 60 ns, also um den Faktor 4. Die Taktfrequenz von Prozessoren dagegen von 2.5 MHz auf 200 MHz also um den Faktor 80! Neben dem Konzept des Caches und Prefetches kann man Speicher eigentlich nur durch Banks schneller machen. Dieses Konzept ist nicht neu: Schon bei der Cray 2 hat man einen Rechner mit hoher Taktfrequenz so an Speicher mit langsamer Zykluszeit angeschlossen. Das Konzept ist folgendes: Anstatt einem Speicher nimmt man z.B. 4 Speicherbänke die je ein Viertel des Speichers abdecken. Intelligenterweise verschiebt man diese so, das man folgenden Zugriff hat:
| Adresse | Bank | Takt |
| 4711 | 0 | 1 |
| 4712 | 1 | 2 |
| 4713 | 2 | 3 |
| 4714 | 3 | 4 |
| 4715 | 0 | 5 |
Wenn man also Code in die CPU holt greift man nacheinander auf alle Bänke zu - in der Regel springt man bei Programmen nicht wild in der Gegend herum sondern arbeitet zumindest einige Befehle linear oder wiederholt in einer Schleife ab. Bei diesem Konzept mit 4 Banks greift also der Prozessor bei jedem 4.ten Takt erst wieder auf denselben Speicher zu. Dieses Konzept wird durch Sprünge, Zugriff auf Daten (die nicht immer ideal liegen müssen) oder Unterprogrammaufrufe allerdings durchkreuzt, in diesem Fall muss der Prozessor warten, wofür sich schon bald den Ausdruck "Wait State" einbürgerte.
Schon beim 8086/88, dem ersten 16 Bitter von Intel wurde daher eine Technik eingeführt um
den Prozessor vom Speicher etwas unabhängiger zu machen. Das erste war, das Bus und
Ausführungseinheit voneinander getrennt waren. Beim 8086 waren die Befehle daher nicht mehr ein
Vielfaches von 4 Takten, denn wenn nur Daten geholt wurden, hatte die Ausführungseinheit bisher
nichts zu tun. Nun konnte sie schon an das Dekodieren gehen, während zugehörige Operanden erst
aus dem Speicher geholt wurden. Dazu diente auch ein kleiner Zwischenspeicher die Prefetch Queue.
Diese Technik wurde in der Folge verbessert. Heute läuft ohne Prefetch nichts mehr. Eine Prefetch
Queue ist im einfachsten Fall nur ein kleiner Zwischenspeicher der einfach nur linear Bytes vom
Speicher holt. Arbeitet der Prozessor zum Beispiel bei Adresse 4048, so holt der Prozessor
einfach schon mal die Bytes 4049,4050,4051... Da Programme normalerweise linear abgearbeitet
werden ist das auch sinnvoll. Wenn allerdings der Prozessor Daten braucht die etwas weiter
entfernt sind oder eine Verzweigung im Code kommt, dann nützt dieser Zwischenspeicher nichts und
es dauert länger ihn wieder neu zu füllen, als wenn man direkt auf den Speicher zugreift. Im
Mittel gibt es jedoch einen deutlichen Performancegewinn.
Weiterhin dauerte das Schreiben auf den Speicher länger als das Lesen, außerdem haben Hochsprachenprogramme sehr oft Befehle bei denen die Daten geholt werden, die gerade erst gespeichert wurden. Damit der 8086 nun nicht die veralteten Daten aus der Prefetch-Qeue holte hatte er einen kleinen Schreibpuffer. Die heutigen SD-RAM's haben eine ähnliches Prinzip um die "niedrigen" Zugriffszeiten zu erreichen: Wenn der Rechner Daten haben will, so braucht dies genauso lang wie bei normalen EDO-RAM's, also etwa 55 ns. Danach liefert SD-RAM die nächsten 8 Bytes in schneller Folge nach, da er vorschauend diese schon ausliest - nun in erheblich kürzerer Zeit von 7-10 ns. Braucht der Prozessor diese aber nicht oder wechselt er aus anderen Gründen die Adresse, so ist SD-RAM wieder bei dem langsamen 55 ns Erstzugriff. Das ist auch der Grund warum die Übergänge in den Architekturen (EDO-RAM -> SD RAM -> DDR RAM und das Hochfahren der Taktzeiten für diesen "Burst" Modus so wenig auf die Anwendungen durchschlagen - Im Mittel kommt z.B. von 100 % mehr Daten bei DDR RAM noch 36 % an, wenn man die Zugriffszeit vor dem Burst dazurechnet. Anwendungen werden sogar nur um 10-20 % schneller.
Eine Lösung ist für das Problem derzeit nicht in Sicht. Man kann nur versuchen das Zugriffsverhalten zu optimieren (siehe auch zum Cache) weiter unten. Evt. übernimmt man Anleihen von den Grafikchipsätzen die sehr stark auf schnelles RAM angewiesen sind. Neben sehr schnellem RAM findet man dort auch bis zu 256 Bit breiten Busse und die Chips sind fest eingelötet anstatt über Module mit langen Leitungswegen und kapazitiven Widerständen angebracht. Dadurch werden Bandbreiten bis 20 GB/s erreicht, während im PC Bereich zum Zeitpunkt des Artikels bei 2.7 GB/s Schluss ist. Angesichts sinkender Preise und immer kürzeren Zyklen bei neuen Mainboards, könnte fest eingelöteter Speicher mit besserer Zugriffszeit, zumindest für bestimmte Anwendungsgebiete interessant erscheinen.
Der 80286 verfügte um erheblich längere Prefetch Queues und größere Schreibpuffer. Die
Performancesteigerung gelang vor allem aber durch schnellere Befehlsausführung. Mit dem 386 kam
nun eine erhebliche Änderung im Aufbau des Prozessors. Er wurde zu einem 32 Bit Prozessor, doch
dazu später mehr. Der 386 war aber vor allem der erste Prozessor der erheblich schneller als das
RAM war. Damit nun der 386 überhaupt schneller als ein 286 war, musste man dies ausgleichen
können und führte das Konzept des Caches ein. Damals war er noch separat auf dem Motherboard
untergebracht. Der Prozessor selbst greift zuerst auf den Cache zu. Dieser speichert den Code vom
Hauptspeicher zwischen und hat eine Zugriffszeit die den Prozessor nicht ausbremst. Der Cache
wird von einem Teil des Prozessors, dem Cachecontroller verwaltet. Er bündelt jeweils 16 oder 32
Bytes zu einer "Cacheline". Wenn der Prozessor neue Daten braucht die nicht im Cache stehen so
muss der Cachecontroller diese vom Hauptspeicher laden. Dazu muss er entscheiden welche
Cachelines am längsten nicht benutzt wurden. Dazu hat er eine Indextabelle an der jeweils steht
welche Cacheline zu welcher Adresse gehört.
Heute ist der Cache nicht mehr direkt gemappt, sondern man speichert für eine Indexadresse mehrere Cachelines ab. Man nennt das mehrfach assoziativ. Ein grundsätzliches Problem ist allerdings das man nun entscheiden muss welche Cacheline verworfen werden muss. Dazu hat ein Prozessor heute eine ausgeklügelte Logik an Bord die den LRU (Least Recently Used) Eintrag herausfindet. Auch beim Schreiben landen die Daten zuerst im Cache, außer die Adresse ist außerhalb des Cache Bereiches. Dann spricht man von Write Miss.
Es stellt sich nun die Frage, wie Cache so schnell sein kann, wenn ich doch oben geschrieben habe, das Speicher im allgemeinen eher langsam ist. Nun die Antwort ist simpel, es wird eine andere Technologie verwendet. "Normaler" Speicher oder DRAM besteht aus einem Kondensator und einem Transistor der es erlaubt die Ladung dieses Kondensators auf den Datenbus zu legen. Das Entladen und Laden dauert aber jedem Kondensator eine kurze Zeitspanne. Cache Speicher (SRAM) bestehen aus 6 (L2) bzw. 8 (L1) Transistoren, die eine Ladung in einem Flip-Flop speichern. Diese können wesentlich schneller ausgelesen und beschrieben werden als DRAM. Dafür ist der Flächenverbrauch für ein Bit viermal so groß.
Mit dem 486 wurde der Cache in den Prozessor integriert und L1 Cache genannt, ein zweiter Cache auf dem Motherboard wurde zum L2 Cache. Das Prinzip: Sind die Daten nicht im kleinen aber schnellen L1 Cache, so sind sie vielleicht im größeren und etwas langsameren L2 Cache, und erst dann muss man auf den Speicher zugreifen. Bei einem Designs wie dem K6-III oder Alpha gibt es sogar L3 Caches mit bis zu 2 MByte Größe. Auf den Prozessorbildern der unten stehenden Bilder der Pentium III / 4 und Prozessoren fallen die Caches durch die Felder mit gleichmäßiger hoher Reflexion auf. Während heutzutage alle Prozessoren Caches haben (Bei Motorola z.B. mit der MC 68030 eingeführt), sind diese bei Mikrocontrollern eher selten. Grund dafür ist der Preis - Die Caches in SRAM Technologie haben erheblich mehr Transistoren als die Prozessorkerne, so das die Ausbeute pro Waver sinkt. Darüber hinaus benötigen die CPUs so mehr Strom.
Die Verwaltung von Caches, sowie ihr schneller Zugriff ist heute essentiell für die Performance eines Prozessors. Ein Großteil der Entwicklung und Chipfläche geht heute auf die Optimierung der Caches drauf. Was der Cache ausmacht kann man leicht selbst ausprobieren. Man kann im BIOS nämlich diese ausschalten. Macht man das so ist auch der neueste Pentium 4 nicht mehr viel schneller als ein langsamer 486 er (dieser aber mit Cache).
Eine Untersuchung von ct beim Pentium 4 mit 3.06 GHz ergab, dass dieser bei den meisten Programmen intern nur auf den Speicher wartete. Im Worst Case (Daten weder im L1 noch L2 Cache und auch nicht über Burst einlesbar) dauerte es 124 ns bis die Daten heran tuckern - In dieser Zeit hätte der Prozessor 380 Instruktionen ausführen können. Der Grund liegt daran, das heutige Architekturen wie SD-RAM, DDR-RAM oder RAMBUS zwar sehr schnell die Daten übertragen wenn sie anliegen, die Zugriffsgeschwindigkeit aber sehr schlecht ist. Abhilfe könnten hier entweder RAM's mit höherer Zugriffsgeschwindigkeit bieten, wie sie z.B. für Grafikkarten eingesetzt werden, vielleicht als zusätzlicher L3 oder L4 Cache. Billiger geht es indem man mehrere Bänke bestückt und die Zugriffe auf diese verteilt. Dies wird bei größeren Servern so gemacht. Ein PC kommt aus Preisgründen meistens nur mit einem RAM Riegel und hat meistens auch nur 2-3 Steckplätze für Speicher.
Mit dem 386 zog auch eine neue Möglichkeit in den Prozessor ein: Virtueller
Speicher. Obgleich der 386 4 Gigabyte Speicher adressieren konnte spendierte ihm Intel eine MMU.
Diese Memory Management Unit tat nun nichts anderes als für verschiedene Programme ihnen jeweils
vorzuspielen, sie hätten den gesamten 4 Gigabyte Adressraum für sich alleine. Dabei nutzt sie
dafür eine 48 Bit Adresse. Die MMU mappte diese dann im realen Speicher oder wenn dieser nicht
reichte informierte der Prozessor das Betriebsystem, das er nun den Speicher von Programm ×
gerade braucht und dieses doch den Inhalt mal auf die Festplatte auslagern sollte. Damit dies
nicht in zuviel Arbeit ausartet wurde der Speicher in kleine Scheiben von 4 Kilobyte oder 4
Megabyte aufgeteilt und jeweils die Belegung von einer 4 Kilobyte Scheibe in einer Tabelle, der
TLB gespeichert.
Mit dem Pentium-Pro (P6) und den auf seiner Architektur aufbauenden Modellen Pentium II und III wurde dieses Speichermanagement verbessert indem man nun nicht nur 4 MByte und 4 KByte große Seiten ansprechen konnte sondern jede Zweierpotenz, die TLB wurde erweitert und der normale Adressraum von 4 auf 64 Gigabyte erhöht. Alle Windows Varianten bleiben aber noch immer im 4 Gigabyte Rahmen. Es macht also keinen Sinn einen Windows 2000 Server mit mehr als 4 GByte Speicher auszustatten. (Zumal das Betriebssystem die oberen 2 GByte des Speichers für sich reserviert).
Virtueller Speicher ist nichts neues, schon die berühmte VAX hatte ihren Namen 1977 nach diesem Konzept bekommen. Beim Intel 386 wird vom Prozessor z.B. ein 48 Bit breiter virtueller Speicher unterstützt. In diesem kann es sehr viele Prozesse mit max. 32 Bit Breite (4 GB) geben. Die Auslagerung übernehmen Betriebssystemroutinen die vom Prozessor über bestimmte Interrupts angestoßen werden, wir haben es hier also mit einer Hard/Softwarelösung zu tun - entsprechend hat jedes Betriebssystem eine etwas andere Lösung das Swappen zu realisieren. Allen gemeinsam ist das man auf der Festplatte einen Bereich (Datei, Partion) hat die ausgelagerte Prozesse aufnimmt. Damit kann man:
MOV AX,100 ; 100 in Register AX laden MOV BX,200 ; 200 in Register BX laden ADD AX,BX ; AX und BX addieren, das Ergebnis steht in AX MOV [4567],AX; Das Ergebnis in Adresse 4567 speichern INC CX ; CX um 1 erhöhen DEC DX ; DX um 1 erniedrigen
Die ersten zwei Operationen erfordern die Laden/Speichern Einheit, genauso die vierte. Die dritte und fünfte/sechste beschäftigt die Integer Einheit. Man kann zumindest die Befehle 4 und 5/6 parallel ausführen, wenn man zwei Load/Store Einheiten hat auch die Befehle eins und zwei parallel. So kann man die Geschwindigkeit vervielfachen: In einem Prozessor wie dem Itanium sitzen 17 Funktionseinheiten, die theoretisch 6 Befehle gleichzeitig ausführen können.
Idealerweise - Denn in Wirklichkeit gab es einige Hindernisse. Der Maschinencode hat sich nicht geändert, d.h. der Programmierer und Compiler kann nicht entscheiden welche Einheit er benutzen will. Er kann nur durch Befehle die nicht voneinander abhängen bewirken, das diese parallel abgearbeitet werden. Weiterhin war beim Pentium es so, das diese ALU's und FPU's nicht voneinander unabhängig waren. Die Ausführungszeit der langsameren blockierte auch die zweite. Wurde z.B. ein lang dauernder Divisionsbefehl in einer ALU ausgeführt so musste die zweite Däumchen drehen, auch wenn sie nur ein Register um eins erhöhen musste. Schon in den 60 er Jahren hat man aber eine Technik erfunden die dies umgeht: Unabhängige Einheiten die Befehle umgruppieren können (Scheduling) oder in einer anderen Reihenfolge ausführen (Out of Order Execution). Erst bei der P6 Architektur (Pentium Pro) führte dies Intel auch ein..
Prinzipiell könnte man die immer höhere Integration zu mehr Einheiten nutzen, doch schon beim Pentium 4 geht man in dieser Hinsicht einen Weg zurück. Das grundsätzliche Problem ist das alle derzeitigen Mikroprozessoren keinen Weg kennen die ALU's und FPU's nach außen hin sichtbar zu machen. Sowohl für den Compiler wie Assemblerprogrammierer gibt es nur eine Recheneinheit. Er kann nun versuchen die Befehle so anzuordnen das diese parallel ausgeführt werden können. Doch oft ist dies nicht möglich. Bei den x86 Prozessoren z.B. machen die wenigen Allzweckregister es notwendig das man diese gemeinsam für verschiedene Daten nutzt. Zudem kann ein Compiler nie wissen ob der Code auf einem Pentium 4 (eine FPU), einem Pentium II/III (zwei FPU's) oder einem Athlon (drei FPU's) läuft. So ist der Pentium 4 bei Code der zwei FPU's voraussetzte sogar langsamer als ein Pentium 3 mit geringerer Taktfrequenz.
Die Lösung dafür wird seit langem diskutiert. Am sinnvollsten scheint es wie früher bei den Großrechnern jeder Recheneinheit einen eigenen Registersatz zuzuweisen und nach außen hin ansprechbar zu gestalten. Noch radikalere Konzepte teilen den Recheneinheiten Tasks zu - Prozesse die im Betriebssystem verankert sind. Derartige Tasks sind nur für eine Millisekunde aktiv, aber gegenüber den kurzen Codesequenzen die sonst vorkommen sind das richtige Ewigkeiten. So fallen Switches zwischen den Tasks kaum ins Gewicht. Der Vorteil wäre, das ein Rechner für den Benutzer auch unter großer Hintergrundlast immer gleich schnell wäre und Programme mit viel Rechenleistungsbedarf einfach mehrere Tasks starten, so wie z.B. ein Spiel für den Aufbau des Hintergrundes, die Steuerung von Figuren und den Aufbau des Vordergrundes. Da jeder Task unabhängig ist kann man so bei einem Prozessor mit n Recheneinheiten die n fache Performance erreichen. Dies wird z.B. der Alpha 21364 einsetzen. Intel hat beim Itanium einen anderen Weg eingeschlagen und die Befehle in Bündel von 3 gruppiert und ihnen eine Information über Abhängigkeiten eingeprägt.
Eine Befehlsausführung besteht normalerweise aus mehreren Schritten: Daten aus dem
Speicher holen, dekodieren, evt. Operanden aus dem Speicher holen, Ausführen, Ergebnisse
Zurückschreiben etc. Selbst einfache Befehle benötigen so mehrere Takte zur Ausführung. Komplexe
Befehle wie die Division benötigten beim 8086 bis zu 171 Takte. Sie erinnern sich noch an den
Maschinenzyklus?
Um die Geschwindigkeit der Befehlsausführung zu steigern wurden in jeder Intel das Konzept der Pipeline vervollkommnet. Eine Pipeline holt bei jedem Takt ein Byte oder mehrere aus dem Speicher. Dann beginnt bei jedem Takt ein weiterer Schritt der Ausführung eines Befehls. Benötigt ein Befehl 5 Takte zur Ausführung so kann man mit einer fünfstufigen Pipeline pro Takt einen Befehl ausführen. Man kann also die Geschwindigkeit der Ausführung steigern - allerdings nur solange wie linear abgearbeitet wird. Verzweigt ein Programm so sind alle Befehle in der Pipeline ungültig und es dauert lange bis diese wieder gefüllt ist und wieder schnell arbeitet. Man nennt dies auch einen "Pipeline Stall". Dies wurde beim neuen Design des Pentium-Pro verhängnisvoll. Er bekam einen Pipeline Stall wenn man in ein 16 Bit Register schrieb und kurz darauf einen 8 Bit Wert aus einer Hälfte des Registers auslas. Intel ging beim Design davon aus, das die Ankündigung von Microsoft stimmte, das neue Windows 95 wäre ein 32 Bit Betriebsystem und maß diesen in Windows 3.1 oft verwendeten Befehlsfolgen keine Bedeutung zu. Die Folge: Da Windows 95 noch voller 16 Bit Treiber war ein Pentium-Pro 166 plötzlich nur noch so schnell wie ein Pentium 120 war.
Beim Pentium Pro wurden die Pipelines der einzelnen Einheiten auch voneinander entkoppelt, allerdings wird dies Intel beim ersten 64 Bitter "Itanium" wieder zurücknehmen. Bei der langen Pipeline des Pentium 4 verwundert es nicht, das er eine 8 mal größere Tabelle für Sprungvorhersagungen hat - Ein Pipeline Stall verlangsamt den Prozessor um so mehr, je länger die Pipeline ist.
Dies bringt einen Performancegewinn bei allen Operationen bei denen mehrere Werte simultan denselben Rechnungen unterzogen werden wie z.B. JPEG Codierung / Dekodierung. Bei der normalen "Wald und Wiesen Applikation" jedoch eher nicht. Es lohnt sich daher diese beiden Architekturen die man auch auf anderen Prozessoren findet genauer zu betrachten.
AMD's 3D-NOW erweiterte dieses Konzept im wesentlichen darauf das man nun auch 2 Fliesskommazahlen mit einfacher Genauigkeit (32 Bits) benutzen konnte, das ist für manche Spiele ganz interessant, für die meisten Zwecke ist jedoch eine erheblich höhere Genauigkeit nötig. (Eine 32 Bit Fließkommazahl hat nur eine Genauigkeit von 7 Stellen, beim Rechnen mit Millionen Beträgen gibt es daher schon Rundungsfehler im Pfennig Bereich, aber wer Millionen hat kann sich sicher auch einen schnelleren Rechner leisten...)
Bei SSE, hat man das erkannt nun endlich Nägel mit Köpfen gemacht:
VLIW steht für Very Long Instruction Word und ist wohl bei der Itanium Architektur am besten erklärt. Hier besteht ein Befehl eigentlich aus 3 Befehlen die zusammen ein 128 Bit "Packet" bilden. Jeder Befehl ist 41 Bits breit und in weiteren 5 Bits steht drin welche Daten (Integer, Fliesskomma, Daten) es sind und wie sie voneinander abhängen. Der Gedanke ist so: Der Prozessor holt sich ein 128 Bit Packet. Danach kann er anhand der 5 Statusbits sofort jeden Befehl an die richtige Ausführungseinheit weiterleiten und diese parallel ausführen. Die gesamte Logik die bisher bei der I32 Architektur diese Verteilung der Befehle gemacht hat kann entfallen. Bei DSP Prozessoren die normalerweise nicht so hoch integriert sind funktioniert dieses Konzept sehr gut, die TMS 32064 Serie erreicht so max. 8 Instruktionen/Takt, während ein Pentium 3 nur zirka 3 Instruktionen pro Takt als Maximum erreicht. Bei Intels Itanium ist der große Geschwindigkeitsgewinn durch VLIW bislang ausgeblieben. Der Nachteil von VLIW ist: Wenn es nicht möglich ist die Befehle parallel auszuführen (z.B. wenn es mehrere Fliesskommabefehle sind die voneinander abhängen), dann muss der entsprechende Codeteil leer bleiben. VLIW Code ist also immer umfangreicher als normaler Code.
SIMD und VLIW sind praktisch nur nutzbar, wenn ihr Compiler dies leistet. Meine Experimente zum Thema Benchmark ergaben hier bei C Compilern, dass diese in der P6 Einstellung nur Befehle umstellen, jedoch keinerlei MMX, 3D-NOW oder gar SSE Befehle verwenden. Dazu müsste man auf Compiler von Intel mit trickreichen Optimierungen ausweichen. Solange aber das Gros der Entwickler mit Visual Studio, CBuilder oder Watcom arbeitet muss man sich nicht wundern wenn man MMX und SSE in keiner Applikation findet.
Beide Konzepte ergeben zumeist die höchste Performancesteigerung bei so genannten "Streaming" Operationen: Wenn ein Datenstrom einer Reihe von Operationen unterworfen wird, die relativ einfach sind. Das sind heute Dinge wie Kompression/Dekompression, Codierung/Decodierung oder allgemein die Bearbeitung von Grafik. Daher etablierten sich beide Konzepte zuerst in Signalverarbeitungsprozessoren die Spezialisten auf diesem Gebiet sind. Eine normale Windows Wald-und-Wiesen Applikation wird durch beide Konzepte kaum schneller, da man hier sehr viele Codeteile durchläuft, die schwerer optimierbar sind, als die kleinen Routinen von Decodern.
Der Pentium 4 hat so 128 interne Register also 16 mal mehr als der Programmierer direkt sehen kann. Beim Sledgehammer - dem ersten 64 Bit Prozessor von AMD geht man einen anderen Weg und verdoppelt im 64 Bit Modus die für den Programmierer nutzbaren Register auf je 16 Fliesskomma und Integer Register. Da ein Compiler meistens durch den Quellcode etwas schlauer ist, welche Variablen man häufig braucht und welche nicht ist es immer geschickter wenn der Prozessor direkt mehr Register zur Verfügung stellt. Bei einer alten Architektur wie der von Intel ist dies schwer möglich da man ja in den Opcodes mehr Bits braucht um die größere Anzahl von Bits zu codieren.
Das Register Renaming hat aber auch andere Vorteile, wenn es darum geht die CPU unabhängiger vom Speicher zu machen. Der Grundgedanke ist mehr Register auf dem Chip zu haben als man als Programmierer anzusprechen und zwischen diesen umzuschalten. Es gibt hier verschiedene Konzepte. Verwirklicht ist schon das Windows Konzept: Wenn ein Prozessor ein Unterprogramm ausführt, dann benötigt dieses einen Teil der Register um Daten zu verarbeiten. Bei herkömmlichen Architekturen "rettet" man diese Register auf den Stack - Einen Hauptspeicherbereich der mittels eines speziellen Registers adressiert wird und von dem man nur die letzten Daten holen kann und restauriert diese nach der Ausführung des Unterprogramms wieder. Auch wenn die Befehle durch die Adressierung über einen Zeiger der vom Prozessor verwaltet wird keine Speicheradresse benötigen fallen doch Zugriffe auf den Hauptspeicher an, der langsam ist.
Das Windows Konzept, z.B. verwirklicht in der SPARC CPU teilt dagegen die Register in globale - immer ansprechbar - und lokale ein. Die lokalen Register werden bei einem Unterprogrammaufruf einfach durch andere ersetzt, deren Inhalt bleibt erhalten und wird nach der Ausführung durch Setzen eines Zeigers auf die ursprünglichen Register wiederhergestellt. Bei einer schon erhältlichen CPU ist so ein Fenster 16 Register breit aus einem Satz von 128 Registern, d.h. es ist maximal eine Schachtelungstiefe von 128 / 16 = 8 möglich. Dann muss auch diese CPU Daten auslagern.
In gewisser Weise ist das Registerrenaming eine Art Register-Cache auf der CPU, jedoch erheblich transparenter als die Verwaltung des Caches. Es ist damit zu rechnen, das man dies noch extensiver nutzen wird.
Mit dem Pentium hat Intel den Prozessorkern auf eine RISC Einheit umgestellt.
Seitdem zerlegt der Prozessor seine ankommenden Befehle in einfachere RISC Operationen und mappt
die vierzehn 80x86 Register auf 32 interne RISC Register um. Dies machen auch die Konkurrenten
Cyrix und AMD, weshalb die Prozessoren nicht mehr mit den Taktfrequenzen vergleichbar sind, da
jeder RISC Kern bestimmte Befehle besser ausführt als ein Konkurrenzprozessor, der dafür andere
Stärken hat. Beim Pentium 4 werden anders als beim Pentium II/III die fertig übersetzten Befehle
im L1 Cache gesammelt, diese haben eine Breite von 118 Bits, also erheblich mehr als die meisten
8086 Befehle.
So ist ein Pentium II/III heute nichts anderes als ein komplexer Emulator, der so tut als wäre er ein 386 er. Erstaunlich ist das es Intel damit zwar nicht gelingt den schnellsten Prozessor zu bauen, aber doch in der Oberliga mitzuspielen. Doch einen Preis muss Intel aufbringen: Es gelingt nur mit einem sehr komplexen Chip (Intels Pentium 4 (Bild links) hat 42 Millionen Transistoren, der schnellere Alpha 21264 dagegen nur 15) und hohen Taktfrequenzen (1.4 MHz für die gleiche Performance die der Alpha bei 700 MHz erreicht). Wenn man allerdings durch enorme Stückzahlen die Serienkosten senken kann und die Entwicklungskosten umlegen kann, spielt dies keine Rolle. Dagegen musste die Konkurrenz bis auf AMD die Segel streichen.
Ein Pentium 4 hat dieses Spiel auf die Spitze getrieben. Intern ist ein Pentium 4 ein RISC Prozessor mit 7 Einheiten - anstatt einer ALU und einer Bus Interface Unit beim 80x86. Er kann z.B. 4 ALU's gleichzeitig einsetzen. Intern verfügt der Prozessor auch über 128 Register die er auf die nach außen sichtbaren 8 User Register spiegelt (Register Remapping). Nach dem Dekodieren der 80x86 Befehle landen diese im L1 Cache der nur RISC Operationen speichert. Je nach Befehl 1-4 im Regelfall (2 im Durchschnitt).
Doch was hat der Benutzer, der Programmierer davon? Er sieht 14 Register, nicht 128. Er sieht eine ALU nicht 4 und muss durch trickreiche Umstellungen des Codes dafür sorgen das diese mit voller Geschwindigkeit arbeiten. Daher spricht Intel auch immer vom optimierten Code. Das Dumme ist nur es gibt nicht den optimierten Code. Ein optimierter Code für den Athlon ist ein anderer als der für den Pentium III und wieder ein anderer für den Pentium 4. So ist der Pentium 4 bei 1.4 GHz langsamer bei SSE Befehlen als ein Pentium III - dieser hat zwei SSE, der P4 nur eine, weshalb Code der abwechselnd zwei SSE ansprechen will langsamer ist. Dagegen ist mit Erweiterung dieses Designs beim Pentium Pro das Programmieren in Assembler schwieriger geworden: Es ist seitdem nicht mehr möglich die Ausführungszeit in Takten präzise anzugeben: Sie hängt davon nun ab ob der Befehl unabhängig von anderen ausgeführt werden kann, ob es keine Probleme in der Pipeline gibt etc. Es gibt nur noch Spannbreiten für die Ausführungszeiten. Dies ist auch der Grund warum Prozessoren innerhalb der x86 Reihe nur schwer vergleichbar sind. Ein Athlon mit 1400 MHz hat also nicht die gleiche Geschwindigkeit wie ein Pentium III oder 4 bei derselben Taktfrequenz. Daher findet man öfters ein so genanntes "Rating", also eine Vergleichsangabe wie z.B. bei den Athlons XP.
2001 kommt auch bei Intel der Einstieg in die 64 Bit Generation die andere Firmen
wie Compaq oder MIPS schon vor Jahren vollzogen haben. Man kann spekulieren ob es des bleibende
Erfolg der 32 Bit x86 Architektur ist oder die nur zaghaften Bestrebungen von MS ihr 32 Bit
Betriebssystem Windows NT auf 64 Bit zu erweitern welche die Entwicklung so lange hinausgezögert
haben. Aber Intel ist zum ersten mal gewillt einen Schnitt zu machen. Der neue Prozessor hat
einen Kompabilitätsmodus. In dem bisheriger x86 Code weiter funktioniert - allerdings nur so
langsam wie ein Pentium II mit 300 MHz. Neu ist ein neuer 64 Modus mit einer neuen Architektur:
RISC Kern mit konstanten 41 Bit Befehlslängen, 128 Registern. Um beides realisieren zu können hat
man Abstriche bei den vielen Features gemacht. Die 4 ALU's und 2 FPU haben z.B. keine
Möglichkeiten mehr Abhängigkeiten zu erkennen und Befehle umzugruppieren - dies hat Intel auf den
Compiler ausgelagert, siehe Abschnitt über VLIW. Anders hätte man den 64 Bit Prozessor samt 32
Bit x86 Kern nicht in 25 Millionen Transistoren fertigen können - zum Vergleich der aktuelle
Pentium 4 hat 42 Millionen. Mehr dazu in einem eigenen
Aufsatz.
Doch noch vor Erscheinen hat der 64 Bit "Itanium" Chip schon Konkurrenz bekommen: AMD's neuer 64 Bit Prozessor "Sledgehammer" ist weitgehend x86 Kompatibel. Einige Opcodes mussten im 64 Bit Modus umdefiniert werden, aber der Programmierer hat im Prinzip denselben Befehlssatz wie beim x86 vor sich. Auch die Register wurden nur um je 8 Fliesskomma und Integer erweitert. Dafür benötigt die 64 Bit Erweiterung nur 5 % zusätzliche Chipfläche. Es wird spannend zu sehen welches Konzept sich durchsetzten wird.
Ein anderer Weg ist ganz einfach von vornherein unabhängige Prozesse zu nehmen - und die gibt es schon heute genug. Jedes Betriebssystem hat selbst wenn gar nichts passiert einige Dutzend von einander unabhängigen Threads also unabhängigen Programmen, und wenn ein Prozessor für jeden Thread eigene Register hat so können diese parallel ausgeführt werden. Diesen Ansatz verfolgte z.B. Compaq bei der Prozessorentwicklung. Entscheidend ist, das man Threads die nichts zu tun hat schnell durch andere ersetzen kann die Rechenzeit benötigen, d.h. das vor allem das Wechseln der Register schnell geht. Platz genug für viele Register ist heute schon vorhanden - schlussendlich kommen heute Prozessoren schon mit 96 KByte L1 und 256 KByte L2 Cache on Chip.
IBM will die Geschwindigkeit durch mehr Bandbreite und unabhängige Einheiten erbringen. Eine neue Architektur soll auf einem Chip bis zu 16 Prozessoren beinhalten, die über einen schnellen Bus verbunden sind und einen gemeinsamen Speicher adressieren. Dies geht nur mit breiten Datenpfaden, wenn man die Geschwindigkeit intern nicht enorm hoch treiben will. Dieser Ansatz ist so neu nicht. Bei Großcomputern setzte man schon früh auf Busse von bis zu 256 Bit um so mehr Daten auf einmal zu verarbeiten. Betrachtet man die Interna des P4, wo der Addierer 4.5 GOps schaffen soll, aber durch die Datenpfade nur max. 3.6 GOps transportiert werden so scheint dieser Ansatz nicht so dumm zu sein. Vor allem macht er wenig Architekturveränderungen bei höherer Integrationsdichte nötig - man bringt einfach mehr Prozessoren auf dem Chip unter.
Alle Konzepte setzen mehr oder weniger auf RISC: Es ist schon schwierig genug dem Code trickreich auf mehrere Prozessoren umzulegen. Wenn man nun auch komplexe Befehle hat wird es sehr schwierig. Auch unterstützen die Prozessoren mehr und mehr Hochsprachen indem z.B. Register eingeblendet werden können - für eine Unterprozedur die ihre eigenen Werte hat. Fast alle Designs gehen auf mehr Register oder längere Breiten die dann für SIMD Operationen wie MMX oder SSE genutzt werden. Auch die Verlagerung der Intelligenz vom Prozessor auf das Betriebssystem - Bearbeiten von einzelnen Tasks pro Funktionseinheit beim Alpha 21364 oder auf den Compiler - Die EPIC Architektur die im Code Abhängigkeiten der Befehle speichert zeigt eine Tendenz zu einem einfacheren, linearen Design.
Vieles ist eigentlich nichts neues, sondern vor Jahren schon in Jahrzehnten in Großrechnern eingeführt. Auch das Arbeiten von mehreren Prozessoren zusammen gab es schon Mitte der 80 er Jahre bei den Transputer Chips von Inmos. Ideal wäre es wenn man im Quellcode die einzelnen Einheiten ansprechen könnten. Doch wäre damit nicht viel gewonnen - In 23 Jahren Intel Entwicklung stieg die Zahl der Einheiten pro Prozessor von 2 auf 17. Ein neuer Prozessor hätte auch Nachfolger mit noch mehr Einheiten die man dann wiederum nicht direkt über den Quellcode ansprechen könnte.
Wenn bisher vor allem von
Prozessoren und auch hier vor allem von der Intel Serie die Rede war, so vor allem um die
Entwicklung über 30 Jahre aufzuzeigen. Darüber hinaus sind diese Prozessoren auch jedem Anwender
bekannt. Doch kein Artikel über Rechnerarchitekturen kommt ohne die Beschreibung von
Mikrocontrollern und DSPs aus.
Der Mikroprozessor wird im amerikanischen Sprachgebrauch oft als CPU (Central Processing Unit = Zentrale Verarbeitungseinheit) abkürzt, was seine Funktion besser beschreibt als das Wort Prozessor. Er ist die Rechenzentrale ihres PCs, doch er ist nicht alleine und kann auch nicht alleine arbeiten. Sie brauchen mindestens noch ein bisschen ROM (um zumindest den Rechner starten zu können und von außen vielleicht ein größeres Programm laden zu können), und auch noch RAM für Daten. Darüber hinaus muss der Prozessor mit der Außenwelt kommunizieren. Er hat dazu einen Steuerbus. Der alleine nützt ihm allerdings nichts, denn die Peripherie hat meistens andere Anschlüsse und verlangt ein Protokoll. Bei der Seriellen Schnittstelle zum Beispiel ein Handshake, das Quittieren jedes Signals. Dies kann der Mikroprozessor erledigen. Außer bei kleinen System machen dies jedoch spezialisierte I/O Bausteine die es fertig konfektioniert für parallele und serielle Schnittstellen gibt. Die originale IBM PC Tastatur hatte sogar einen eigenen Prozessor zur Verarbeitung der Daten.
Diese Chips machen auch die Hauptmasse der Kosten eines Mainboards aus, wobei dies heute durch wenige Bausteine noch relativ preiswert ist. In den 80 er Jahren als es noch Dutzende von Bausteinen waren, lag der Preis eines Mainboards noch in der Größenordnung von 1000 DM.
Für zahlreiche Anwendungen wo der Prozessor nur steuern soll und kein PC mit flexibler Ausbaufähigkeit verlangt wird, ist dies zu aufwendig und zu teuer. Daher kam man bald auf die Idee die wesentlichen Peripheriebausteine auf dem Prozessor mit zu integrieren. Einen solchen Prozessor bezeichnet man dann als Mikrocontroller. Er enthält typischerweise einige programmierbare Ausgangsports die den I/O Bausteinen entsprechen und an die man Peripherie oder Messwertwandler anhängen kann. Dazu kommen einige programmierbare Timer, dies sind Zeitgeber die nach Ablauf einer bestimmten Frist den Prozessor veranlassen ein Programm durchzuführen, z.B. regelmäßig die Herzfrequenz in einem mobilen EKG Gerät aufzunehmen. Mit integriert sind zumeist auch vom Anwender programmierbare ROMs und ein RAM auf dem Chip, Ein solcher Mikrocontroller ist somit anders als ein Prozessor autark.
Man findet Mikrocontroller häufiger als man denkt. Ein Großteil der Prozessoren die ein einem heutigen Haushalt ihre Arbeit verrichten sind solche Mikrocontroller. In einem amerikanischen Haushalt sollen es über 100 sein, davon alleine 40-50 im Auto. Mikrocontroller steuern und überwachen. Von einfachsten Aufgaben wie die Steuerung einer Mikrowelle oder Waschmaschine über Telekommunikation (Handys) bis hin zu schnell reagierenden Echtzeitsystemen (Motorsteuerung, Airbag, Herzschrittmacher).
Die fortgeschrittene Form eines Mikrocontrollers ist das System on a Chip oder Embedded System. Man versteht dabei auch das Integrieren weiterer Komponenten, im Extremfall kann man einen 486 PC mit RAM, VGA Grafik und Windows im Flash RAM auf einem Chip unterbringen. Der früher in Heimcomputer eingesetzte Z80 Prozessor (Bild rechts) ist heute als integrierter Baustein der als Webserver fungieren kann erhältlich. Er bietet 15 Internetprotokolle, Ethernetanschluss und ist Javafähig. Solche Systeme sind es die den internetfähigen Kühlschrank ermöglichen werden. PCs sind dazu viel zu teuer (außer sie bekommen einen PC für 11 Dollar, soviel kostet ein eZ80 Webserver).
Die nahe liegende Idee einen Prozessor zum Mikrocontroller umzubauen hat sich nicht durchgesetzt. Vor allem ist hinderlich das der Befehlssatz nach wie vor die integrierten Bausteine als externe Quellen anspricht. Man findet Mikrocontroller auf Basis der 80186 und Power PC Chips nur dort wo die Entwicklung auf der PC Plattform eine Rolle spielt (80x86 Serie) oder hohe Rechenleistungen gefragt sind (Power PC). Erfolgreich waren spezielle Eigenentwicklungen als Mikrocontroller wie die 8051 Serie von Intel oder die ursprünglich aus der erfolglosen MC 6800 Serie entstandenen MC68HC Prozessoren von Motorola. Auch andere PC Prozessoren wie die ARM Chips wurden als Mikrocontroller weiterentwickelt.
Deswegen wurden die oben angeführten Techniken VLIW und SIMD bei Digitalprozessoren zuerst eingesetzt. Diese Architekturen erlauben es mehrere Daten auf einmal zu bearbeiten. Digitale Signalprozessoren findet man heute sehr häufig in Geräten der Unterhaltungselektronik. Vom Handy über den CD Spiele bis zum DVD Spieler. Daneben auch wo Daten zeitkritisch ausgewertet werden müssen: In Motorsteuerungen und Herzüberwachungsnotfallhandys. Welche Leistung Signalprozessoren haben kann jeder nachprüfen. Zum Zeitpunkt dieses Artikels kostet ein Stand-Alone DVD Player mit Signalprozessor zum Decodieren der Signale 140 Euro. Bei einem PC muss schon ein Prozessor der 500 MHz Klasse arbeiten damit er DVDs ohne Hardwareunterstützung dekodieren kann und er kostet deutlich mehr als 140 Euro.
Zudem sehen PC Prozessoren bei typischen DSP Anwendungen sehr schlecht aus. Im Telemark Benchmark, einem Telekommunikationsbenchmark kommt die Embedded Variante des Power PC Prozessors, der MPC7447 auf einen Wert von 163.9 bei 1.3 GHz Takt. Der MIPS Prozessor Fastmath dagegen bei 2 GHz auf einen Wert von 868.3 - mehr als 5 fache! Das brachte eine Forschergruppe auf die Idee 70 Playstation 2 zusammen zu schalten - den der Grafikprozessor darin hat eine Spitzenleistung von 6.2 GFLOP. Bei speziellen Anwendungen ist dies dann ein 400 GFLOP schneller Rechner, der nur 50.000 USD gekostet hat. Für die gleiche Rechenleistung brauchte man 180 Pentium 4 Rechner mit je 3 GHz, die jedoch erheblich teurer wären.
| Kontakt | Neues | Bücher vom Autor | Buchempfehlungen | Gästebuch |
|
|