Prozessorentwicklung – ganz kurz
Vorab: ich weiß das einige Blogleser sich gut in Prozessortechnologie auskennen. Ich selbst habe über die Hardwareentwicklung ja eine ganze Rubrik mit etlichen sehr langen Artikeln geschrieben. Aber wir leben nun mal in den Zwanziger Jahren des dritten Jahrtausends, die nachkommende Jugend. die nur noch gewohnt ist kurze Antworten von Google zu bekommen, wäre mit langen Artikeln geistig hoffnungslos überfordert. Daher habe ich die Prozessorentwicklung auf das Minimale gekürzt.
Dieser Artikel beschriebt einige Stationen der Prozessorentwicklung am Beispiel der x86 Linie. Ich führte aber jeweils noch ein Beispiel aus der Vergangenheit an, denn das meiste hat Intel nicht erfunden.
Intel 8080: Maschinenzyklus, Hardwareverdrahtung
Der Vorgänger der x86 Linie, die wie man an dem Namen erkennt mit dem 8086 beginnt, ist der 8 Bit Prozessor 8080. Doch er eignet sich um zwei grundsätzliche Dinge zu demonstrieren und nicht zuletzt ist der 8086 auch in der Lage ein 8080 Maschinensprachprogramm nach Übersetzung in 8086 Code auszuführen, gehört also noch in dieselbe Familie.
Der 8080 arbeitet wie jeder Prozessor einen Maschinensprachzyklus ab, der besteht aus den drei Schritten:
- Fetch – etwas aus dem Speicher holen
- Decode – Befehl aus dem Bitmuster erkennen
- Execute – Befehl ausführen
Jeder dieser Teile kann aus mehreren Teilschritten bestehen und es kann ein Teilzyklus wiederholt werden. So benötigen Befehle, die Daten laden oder speichern weitere Fetch-.Zyklen.
Das zweite ist das der 8080 hardwareverdrahtet ist. Bei jedem Takt macht er eine Aktion, eine Schaltung schaltet nacheinander, die Schritte die erfolgen müssen, durch. Ein Befehl benötigt so zur Ausführung mehrere Takte, bei dem 8080 mindestens 6.
Intel 8086: Prefetch, Microcode
Als der 8086 erschien, war schon von Bedeutung das die Geschwindigkeit vom DRAM Bausteinen viel langsamer anstieg, als die Taktrate von Prozessoren. Um die Auswirkungen zu dämpfen holte der Prozessor nach einem Fetch-Zyklus gleich das nächste Befehlswort auf der folgenden Adresse ab und speicherte dies zwischen. Intern dekodierte er während dieser Zeit den Befehl, griff also nicht auf den Speicher zu so war dieses Wort schon vorhanden und unter Umständen konnte der nächste Fetch-Zyklus entfallen.
Die bedeutendere Neuerung im 8086 war aber Microcode. Anstatt der Hardwareverdrahtung gibt es in ROM im Prozessor. Der Befehl wird in eine Adresse des ROMs übersetzt und bei jedem Takt wird ein Bitmuster aus dem ROM an die Schaltung welche den Ablauf regelt, angelegt.
Das hat einige Vorteile. Zum einen ist das ROM fertigungstechnisch viel einfacher als die Hardwareverdrahtung, spart also Transistoren. Das zweite ist das es schnell änderbar ist, was in der Entwicklung Zeit spart, bei ausgelieferten Prozessoren von Großrechnern wo es ein eigener Baustein ist, kann man so Fehler korrigieren, heute geht das sogar über automatische Patches übers Internet. Der Hauptvorteil beim 8086 war aber, dass so Befehle realisiert werden konnten, die der Prozessor eigentlich von der Hardware her nicht beherrschte. Das waren beim 8086 zum Beispiel die Divisions- und Multiplikationsbefehle. Diese werden bei Prozessoren, die sie nicht als Schaltung beherrschen, durch ein kleines Programm realisiert. Genau so ein Programm war im ROM abgelegt, aber da die Zyklen für Fetch und Decode wegfielen, war es viel schneller.
Ein prominenter Einsatz von Microcode bei Großrechnern war das IBM System 360, bei dem durch das Microcode-ROM alle Mitglieder der Familie denselben Code ausführen konnten, nur war das Microcode-ROM auf die Fähigkeiten der jeweiligen Maschine angepasst.
80286 – Pipeline
Ein sehr einfacher Schritt einen Prozessor zu beschleunigen ist die Pipeline. Im oben erklärten Maschinenzyklus ist zu jedem Zeitpunkt eine andere Einheit des Prozessors aktiv. Im Fetch der Ein-/Ausgabeteil, bei Decode derer Befehlsdekoder und bei Execute kann es abhängig vom Befehl variieren. Die anderen Einheiten tun nichts. Bei einer Pipeline wird pro Takt ein Befehl geholt (fetch) und pro Takt durchläuft er weiter den Maschinenzyklus. So sind aber immer mehrere Befehle gleichzeitig in Arbeit. Die Erweiterung ist relativ einfach, so fand die erste bekannte Verwendung schon Ende der Fünfziger Jahre in der ILLIAC II statt.
Beim 80286 hatte die Pipeline drei Stufen, konnte also theoretisch drei Befehle gleichzeitig bearbeiten und hatte daher eine theoretische Maximalperformance der dreifachen 8086 Geschwindigkeit – in der Praxis, weil die Befehle nicht alle unabhängig sind, also ein Befehl das Ergebnis des Vorgehenden benötigt, und so warten muss, ergab sich immerhin eine Geschwindigkeitssteigerung um den Faktor 2,5.
80386 – Caches und 32 Bit
Wie schon bei Großrechnern war es schon Mitte der Achtziger so, dass RAM-Bausteine die Daten gar nicht so schnell liefern konnten, wie sie ein Prozessor verarbeiten konnte. Das ist bis heute so, würde man nicht Technologien wie Caches einsetzen, die Taktrate heutiger Rechner wäre durch das RAM auf 130 bis 200 MHz begrenzt. Ein Cache ist ein Zwischenspeicher aus sehr schnellem, aber teurem RAM. Er wird von der CPU verwaltet, die sich merkt, wo im Cache welche Teile des RAM sich befinden. Er beschleunigt die CPU, weil Programme oft Programmteile wiederholen (Schleifen) oder Unterprogramme sehr häufig anspringen. Befinden sich diese Teile im Cache so kann auf sie mit geringer Verzögerung zugegriffen werden. Heute machen Caches einen Großteil der Fläche eines Prozessors aus. Ein prominenter Einsatz des Caches schon in den sechziger Jahren war ebenfalls beim IBM System 360, wo sie bei den höherpreisigen Modellen für mehr Performance sorgten.
Eine zweite Neuerung des 80386 war die Erweiterung der Architektur auf 32 Bit. Je mehr Bits ein Prozessor verarbeiten kann, desto weniger Befehle braucht er, wenn er mit großen Zahlen arbeiten muss. Viel bedeutender ist aber, dass mit der Bitbreite der Register auch festgelegt wird, wie viel Speicher angesprochen werden kann. 32 Bit reichte für 4 Gigabytes, eine enorme Größe. Als der 386-Prozessor 1985 erscheint, hatten die meisten PC maximal 1 MByte Speicher. So reichte die Architektur auch bis weit nach 2000, erst dann wurde auf 64 Bit nochmals verdoppelt.
80486 – Fließkommaarithmetik
Die bisherigen x86 Prozessoren verarbeiteten nur ganze Zahlen. Wollte man Fließkommazahlen berechnen, so musste man dies per Programm tun, ebenso mathematische Funktionen wie Wurzel ziehen, Sinus oder Logarithmus-Funktion. Seit 1983 gab es einen Zusatzprozessor, den x87 Coprozessor, ein eigener Baustein, den man dafür nutzen konnte, er konnte schon Fließkommazahlen in Hardware verarbeiten, so ein Programm enorm beschleunigen, war aber auch immer teuer. Dieser Prozessor wurde beim 80486 nun integriert.
Dei Verarbeitung von Fließkommazahlen war immer eine Domäne von Spielecomputern für naturwissenschaftlich-technische Probleme. Schon der erste Supercomputer, die CDC 6600 hatte diese Fähigkeit. Großrechner für kommerzielle Zwecke wie Finanzen verzichteten darauf. Der Grund ist relativ simpel: Bei Finanzberechnungen benötigt man zum einen nur die vier Grundrechenarten und wenn man mit hinreichend großen Zahlen rechnet, kann man auch mit Ganzzahlen alle Berechnungen durchführen, man verschiebt einfach das Komma, sodass die Zahl „1“ eben 1 Cent oder 0,01 Euro entspricht.
Pentium – super-skalar
Es wurde schon die Pipeline zur Geschwindigkeitssteigerung erwähnt. Doch sie hat Grenzen. Wenn ein Befehl eine Einheit lange beansprucht, weil er viele Takte zum Durchlauf braucht, steht die ganze Pipeline. Die Idee diesen Flaschenhals aufzulösen ist die Superskalarität, darunter versteht man, dass einzelne Funktionseinheiten mehrfach vorhanden sind. Beim Pentium war die Arithmetisch-Logische-Einheit, die bei normalem Code am meisten ausgelastet ist, doppelt vorhanden. Das beschleunigte die Abarbeitung weiter. Auch dieses Konzept ist nicht neu und wurde schon 1965 in der legendären CDC 6600 eingeführt.
Pentium Pro – out of Order execution
Ein zweiter Nachteil einer Pipeline ist, das Befehle im Code oft nicht unabhängig sind. Bei der Rechnung D = A*B + C muss zuerst A*B berechnet werden, bevor C addiert werden kann. Die zweite Operation ist also von der ersten Rechnung abhängig. Bei Out of Order Execeution sortiert die CPU die Befehle um, nimmt in diesem Beispiel den nächsten Befehl nach dieser Rechnung und zieht diesen vor. Das klingt einfach, doch da es im Code oft viele Sprünge gibt, muss eine ausgeklügelte Logik „vorhersagen“ wohin ein Sprung geht. Das wurde beim Pentium Pro eingeführt und ab dem Pentium II auch in die normale Pentium-Linie übernommen.
Pentium III – SSE
SSE ist ein Intelbegriff ( Streaming SIMD Extension) die allgemeine Bezeichnung ist SIMD, was für Single Instruction, Multiple Data steht. Die Idee ist, dass sehr oft in Programmen viele Zahlen, die dann oft in einem Array stehen, derselben Rechnung unterworfen werden. Bei SIMD arbeitet dann ein Befehl mit mehreren Daten. Vorgängerlösungen für SSE gab es schon ab dem Pentium, aber beim Pentium III wurden erstmals die Register so erweitert, dass doppelt so viele (einfach genaue) Fließkommazahlen auf einmal verarbeitet werden können. Das waren 128 Bit breite Register, die vier 32 Bit Fließkommazahlen gleichzeitig bearbeiten konnten. SSE ist heute zu AVX geworden und die Register sind inzwischen bis zu 512 Bit breit.
Anders als die vorherigen Verbesserungen beschleunigen SSE und AVX nicht alle Programme, sondern nur Programme, die diese Befehle nutzen. Das ist bei der normalen PC-Bürosoftware nicht der Fall, sondern eher bei Supercomputern die Tausende von Prozessoren einsetzen. Auch hier gibt es ein historisches Vorbild: Der Vektor Rechner. Der berühmteste war die 1976 erschienene Cray 1 die Vektorregister hatte die acht Zahlen aufnehmen konnte. Sie arbeitete aber etwas anders als SSE und AVX. Während SSE/AVX ein langes Register in mehrere Teile unterteilen, und diese parallel bearbeiten, wird bei einem Vektorrechner pro Takt eine Zahl aus den Vektorregistern den Rechenwerken zugeführt.
Core Duo – mehrere Kerne
Irgendwann kann man die Geschwindigkeit einer CPU kaum mehr steigern, beim PC gab es einen enormen Geschwindigkeitsgewinn durch interne Optimierungen, aber auch Steigerung der maximalen Taktgeschwindigkeit um den Faktor 500 in rund 25 Jahren. Nach dem Jahr 2000 stieg die Leistung aber kaum noch an. Die Lösung ist, mehrere CPU-Kerne auf einem Chip unterzubringen. Jede CPU hat eigene Teile wie einen eigenen Cache, alle CPU nutzen aber meist noch einen gemeinsamen größeren Cache und die gesamte Ein/Ausgabeeinheit mit der Verbindung zum Chipsatz. Intel versuchte schon vorher beim Pentium 4 mit Hyperthreading eine Lösung zu präsentieren. Bei Hyperthreading wird dem Betriebssystem eine größere Zahl an Prozessorkernen mitgeteilt als tatsächlich vorhandene sind und der Prozessor versucht den Code mehrerer Tasks so zu verteilen, dass alle Funktionseinheiten voll ausgelastet sind. Doch Hyperthreading bringt weitaus weniger Geschwindigkeit als ein zweiter Kern.
Das grundlegende Problem und damit auch die Begrenzung der Kernzahl ist das der Speicher ja immer noch der gleiche wie bei einem Kern ist. Hat man doppelt so viele Kerne so benötigt man auch die doppelte Speicherbandbreite und das scheitert bei Desktop- oder Notebook-PC einfach an der Maximalzahl von DIMM-Steckplätzen auf dem Motherboard. Serverprozessoren, bei Intel die Xeon-Linie, haben andere Motherboards mit nicht zwei oder vier, sondern bis zu 16 DIMM Steckplätzen und können so auch die vielen Daten, die mehr CPU Kerne benötigen, liefern. Mehrere Kerne gab es schon früh bei den Großrechnern, weil die CPU-Baugruppe dort meist nur einen kleinen Teil des Computers ausmachte, konnte man leicht durch deren Verdoppeln die Leistung steigern, ohne dass der Computer gleich doppelt so teuer wurde. Zudem bediente ein Großrechner oft viele Benutzer, sodass man problemlos die Prozesse auf mehr Kerne aufteilen konnte.
SD-RAM und DDR-RAM
Ich habe leider nicht finden können, wann dieses Feature bei der X86 Linie einzog, meine aber, es muss beim Pentium II gewesen sein. RAM war schon immer zu langsam. Bei RAM gibt es eine Zugriffszeit, in der ein Speicherbaustein die Daten intern auslesen muss und die Daten an die Datenleitungen anlegen. Diese Zugriffszeit ist zwar in den letzten 30 Jahren um den Faktor 40 besser geworden, aber die Taktrate der Prozessoren stieg im selben Zeitraum um den Faktor 500. Mit Caches kann man die Langsamkeit des Speichers kaschieren, doch je schneller ein Prozessor wird, desto mehr Daten braucht er und dann ist irgendwann auch ein Cache der Flaschenhals.
SD-RAM basiert wie Cache auf der Eigenschaft von Code, aber auch Daten, dass er sehr lokal ist, also die Wahrscheinlichkeit ist groß, das man sehr bald auch die Daten der nun folgenden Adressen benötigt. SD-RAM hat nun die gleiche Verzögerung der Zugriffszeit beim Anfordern der Daten, dem ersten Zugriff, danach beginnt der Speicher aber automatisch die folgenden Daten auszulesen und an die Datenleitungen anzulegen. Der Prozessor muss sie synchron dazu abholen, woher die Abkürzung SD-RAM (Synchonos Data RAM) kommt. Die Taktfrequenz dafür wird meist in der Bezichnung der Module angegeben. Die ersten Module kamen in der zweiten Hälfte der Neunziger Jahre auf den Markt und hatten eine Taktrate von 66 MHz. Später folgte DDR-RAM, bei dem bei den beiden Flanken eines Taktes jeweils Daten übertragen werden, also die doppelte Datenrate von SD-RAM. Auch hier ist die realisierbare Datenrate Teil der Bezeichnung, ein DDR4-3200 Speicher wird Daten mit 3.200 MHz übertragen (2 x 1.600 MHz).
Auch diese Technik ist nicht neu. Großrechner unterteilten den Speicher in Bänke und sprachen jede Bank nacheinander an, wobei diese meist als Prefetch schon das nächste Wort vorluden. Da so sehr viele – bis zu 256 – Bänke angesprochen wurden, konnten sie praktisch ohne Verzögerung die Daten abholen, sofern auch das Programm linear ablief.
So, das war eine kurze Einführung. Heute sind Prozessoren enorm kompliziert, ich habe einmal den I7-12900 auseinander genommen und in dieser ist schon ziemlich komplex. Vieles habe ich auch weggelassen, so die Einführung der RISC-Maschine.