Die Zahl für heute: 23,12

In dem heutigen Blog geht es erneut um die CPU-Entwicklung und ich will mal die einzelnen Faktoren auseinander dividieren, um dann auf die ominöse Zahl 23,12 zu kommen und zuletzt mal begründen, warum ich den 32-Bit-Mikroprozessor für das Optimum halte.

Fangen wir mal mit dem Nettoergebnis an. Um wie viel ist die Geschwindigkeit eines Computers seit meinem ersten gestiegen? Nun wenn man es genau nimmt, vom ersten (Ti 99/4a) gibt es kein Benchmark, das heute noch laufen würde, aber es gibt vom Z80 Prozessor der in meinem zweiten Rechner steckte ein Ergebnis des Dhrystone Benchmarks. Der erreichte bei 2,5 MHz 91 Dhrystones, skaliert man das auf 4 MHz so sind es 145, in einem Emulator erreichte ein 3,47 MHz Z80 142 Dhrystones. Normiert wird der Benchmark auf die VAX 11/780 die je nach Literatur bei 1560 bis 1757 liegt. Gängig werden 1757 Dhrystones als 1 Dhyrstone-MIPS zu definiert.

Das Problem ist natürlich auch das es den Dhrytsone Benchmark nicht gibt. Ich habe eine alte und eine moderne Version mit dem Cbuilder kompiliert und schon hier gibt es werte von 6332 und 9570 VAX-MIPS pro Kern und ein besserer Compiler erreicht dann wiederum mehr VAX-MIPS. Das ist aber die Problematik jedes Benchmarks.

Mein derzeitiger Prozessor (Core i5-4690) erreicht mit allen vier Kernen über 119.000 Dhrystone MIPS. Er ist also fast 1,5 Millionen mal schneller als mein Z80A Rechner. Nun ist mein heutiger Rechner fast acht Jahre alt, wer will kann bei Sisoft das Systeminformationsprogramm Sandra lite herunterladen welche die Dhrystones berechnet. Warnung: Das dauert einige Minuten, während denen der PC praktisch unbenutzbar ist.

Nun wie kommt die Mehrleistung zustande?

Also zuerst mal hat die heutige CPU vier Kerne ein Z80A natürlich nur einen. Das reduziert dann schon den Vorteil um den Faktor vier auf rund 368.000-fach.

Der zweite offensichtliche Punkt ist die Taktfrequenz. Die CPU-Kerne der heutigen CPU takten mit 3,5 GHz, der Z80 damals mit 4 MHz wobei er netto sogar nur mit 3,2 MHz lief (Wartezyklen durch gleichzeitigen Zugriff auf den Bildschirmspeicher durch das Gate Array). Der Takt ist also um den Faktor 875 kleiner. Nun schrumpft der Vorteil schon auf den Faktor 420 zusammen. Das gilt allerdings nur, wenn der Zusammenhang Taktsteigerung – Geschwindigkeitssteigerung linear ist. Für den Dhrytone Benchmark der relativ klein ist – er muss ja auch von einer Z80 ausgeführt werden können, gilt das. Die Problematik das mehr Takt nicht immer viel mehr Geschwindigkeit bringt, ist aber seit Anfang der Achtziger gegeben. Seitdem ist der Zugriff von Prozessoren immer schneller angestiegen als Speicher Daten liefern können. Schon die Z80 hatte als Abhilfe den Mechanismus des Waitstates, eines Wartetakts. Konnte der Speicher die Daten nicht liefern, so wartete die CPU einen Takt, das war immer noch günstiger als einen kompletten Zyklus (4 Takte) lang zu warten. Wartezyklen schlugen erst richtig bei der 80286 Generation zu. Der IBM PC/AT kostete zwar über 15.000 DM, aber die 8 MHz Version hatte schon einen Wartetakt, weil IBM beim Speicher etwas Geld sparte und nicht die schnellsten Chips verbaute. Konkurrenten lieferten Rechner ohne „Waitstate“ und warben damit. Mit dem 80386 Prozessor wurde das Problem noch größer, der war noch höher getaktet und es zog der Cache ein, Der besteht aus schnellem SRAM, derselben Technologie wie die Register im Prozessor. Allerdings muss ein Cache auch verwaltet werden, das addiert Zeitverluste und befindet sich Daten nicht im Cache, so muss auf den langsamen Arbeitsspeicher zugegriffen werden. Dies zeigt sich, wenn man Datenstrukturen immer größer wählt wie in diesem Test: Die Latenz ist in L1-Cache vernachlässigbar. Sie steigt im L2-Cache auf rund 3 ns – klingt nach wenig, sind aber bei 4 GHz Takt schon 12 Takte, beim L3-Cache sind es 14 ns – 56 Takte (Intel Angabe: 53) und beim Hauptspeicher sind es über 90 ns. Bei dem kleinen Dhrystone Benchmark kann man davon ausgehen, dass er komplett im Cache ist, daher auch hier eine starke Beschleunigung, die aber bei anderen Programmen durchaus nicht so sein muss.

Der zweite große Punkt für eine Geschwindigkeitssteigerung ist die Vergrößerung der Bits die ein Prozessor auf einmal verarbeitet. Der heutige Prozessor ist ein 64 Bit Prozessor, der Z80 war ein 8 Bit Prozessor. Der Nutzen ist offensichtlich bei Berechnungen. Eine Berechnung mit 64 Bit kann der 64-Bit Prozessor in einem Rutsch durchführen, ein 8 Bitter braucht dazu acht Berechnungen die auch mit acht Lade- und Speicheranweisungen gekoppelt sind. Beim Dhrystone Benchmark der mit 32 Bit Zahlen arbeitet sind 8 und 16 Bit Prozessoren performen daher auch relativ schlecht, verglichen mit einem 32 oder gar 64 Bit Prozessor. Doch nicht überall ist der Vorteil sofort offensichtlich. Bei Sprüngen scheint der Vorteil von immer mehr Bits nicht gegeben zu sein. Ebenso bei Textoperationen, die früher immer mit 8 Bit erfolgten, heute mit Unicode mit 16 Bit, weil bei der Sprache C man jedes Zeichen einzeln prüfen muss ob es nicht das Nullzeichen ist, damit wurde das Stringende markiert. Aber – da ein 64 Bit Prozessor auch 64 Bit auf einmal lädt, kann er bei einem Sprung die Adresse in zwei Zugriffen laden, ein 8 Bitter braucht dafür mindestens drei Zugriffe da eine Adresse 16 Bit breit ist (plus ein Zugriff für den Opcode). Ändert sich an den Anforderungen nichts, so ist der Geschwindigkeitsgewinn nicht so hoch. Ich habe ja mal das Benchmark der ct‘ 10/1987 erwähnt das ich dank der langen Lebenszeit von DOS bis hin zu einem Athlon mit 550 MHz testen konnte. Das skalierte relativ linear hoch. Denn DOS nutzte eben von dem 32 Bit Befehlen nichts.

Aber mit jeder Generation kommen neue Befehle dazu. Der Z80 konnte 16 Bit Arithmetik nur eingeschränkt durchführen (Addition, Subtraktion, Inkremente, Dekremente), 8 Bit Arithmetik hatte mehr Möglichkeiten, aber konnte auch nicht multiplizieren und dividieren. Das konnten die 16 Bit CPUs. Mit jeder Generation kamen Befehle hinzu, selbst bei RISC-Prozessoren. Solche Befehle können bestimmte Operationen enorm beschleunigen. Mein Paradebeispiel ist der Z80 Befehl LDIR / LDDR. Dieser Befehl kopiert einen Speicherblock dessen Adresse im Register HL steht in die Adressen ab Register DE, die Länge wird in Register BC angegeben. Ein Z80 Code dafür würde so aussehen:

ld hl,startadresse ; Lade Quelladresse in Register HL

ld de,zieladresse ; Lade Zieladresse in Register DE

ld bc,laenge ; Lade Blocklänge in Register BC

ldir

Würde man denselben Code, ohne den LDIR Befehl schreiben, so wäre eine ganze Sequenz nötig:

ld hl,startadresse

ld de,zieladresse

ld bc,laenge

loop: ld a,(hl) ; lade Wert aus Adresse HL ins Register A

ld (de),a ; lade Wert aus Register A in den Speicher der durch DE adressiert wird

inc hl ; erhöhe Quelladresse

inc de ; erhöhe Zieladresse

dec bc ; erniedrige Zähler

jp nz,loop ; Springe wenn Zähler nicht Null ist an den Schleifenanfang (loop)

LDIR Code
Länge 2 Bytes 8 Bytes
Ausführungszeit pro Byte 21 Takte 42 Takte

Es wird ¾ des Codes eingespart. Relevanter ist die Beschleunigung der Ausführung. Bei der Z80 werden für fünf Befehle die Fetches aus dem Arbeitsspeicher und das Dekodieren eingespart. Da der Sprungbefehl noch einen weiteren Fetch erfordert und jeder Fetch/Decode mindestens 3 Takte erfordert, halbiert sich die Ausführungsdauer.

Wäre die Z80 CPU fähig, mehrere Instruktionen gleichzeitig auszuführen, wie heute üblich, dann wäre beim LDIR Befehl intern viel parallelisierbar – das Erhöhen der Adressen wäre direkt nach den Ladebefehlen möglich. Im Prinzip wären die drei Inkrement/Dekrement Befehle parallel ausführbar. Die Parallelität kann in komplexen Befehlen aber schon intern in dem Microcode genutzt werden, dagegen benötigt man für die obige Schleife eine aufwendige Logik die Abhängigkeiten erkennt. Heutige Prozessoren haben Spezialbefehle für Verschlüsselung und Entschlüsselung die eine noch viel höhere Beschleunigung ermöglichen. Ähnliches findet man bei Vektoroperationen: AVX kann in der neuesten Version AVX512 acht Zahlen in der gleichen Zeit bearbeiten wie mit den normalen Fließkommaoperationen.

Die nächste Stufe ist die interne Optimierung, dies kann man nicht an Befehlen festmachen, sondern an ihren Ausführungszeiten. Und hier komme ich zu der ominösen Zahl 23,12. Sie gibt an, um wie viel schneller ein Befehl intern ausgeführt wird – jetzt unabhängig von der Art und Leistungsfähigkeit des Befehls. Beim Z80 brauchte ein Befehl im Instruktionsmix 6,8 Takte. Die schnellsten Befehle brauchten 4 Takte, die langsamsten 23. Durch eine Pipeline kann man die Ausführungszeit auf minimal 1 Takt pro Befehl drücken, das wurde beim Z80 auch gemacht. Der eZ80 hat eine solche Pipeline und ist dreimal schneller als ein Z80 bei gleichem Takt (also 2,2 Takte im Mix). Mehr geht nur durch interne Parallelität, sprich man führt mehrere Befehle parallel aus, wie ich dies schon beim obigen Codestück angedeutet habe. Der eZ80 reduziert so die Ausführungszeit des LDIR Befehls auf 8 Takte, nutzt man einen neuen Modus in dem er 16 Bit auf einmal kopiert (anstatt byteweise) so sind es sogar nur 6 Takte.

So kam ich auch auf die Zahl 23,12. Die Angabe bezieht sich auf die „Instructions per Cycle“ IPC also Anweisungen pro Takt. Beim Z0 betrug dieser Wert 1/6,8. Ähnlich sah es bei8086 aus (1/7,5), dann wurde es schrittweise besser: 80286: 1/4,92, 80386: ¼, 80486 1/1,95 und der Pentium erreichte als erster Prozessor die magische Zahl 1.

Die modernsten Prozessoren der x86 Architektur (Alder Lake – Core 12.te Generation und Zen 3) erreichen einen IPC von 3,4, daraus ergibt sich eine Beschleunigung des internen Ablaufs in knapp 40 Jahren um den Faktor 23,12. Alles was oberhalb von 1 liegt, ist nur durch interne Parallelität und das Vorziehen von Befehlen möglich. Das ging los im Pentium.

Der 32 Bit Prozessor als Optimum

Zuletzt noch einen Blick, warum ich einen 32 Bit Prozessor für das ideale handele. Die Angabe „n Bit“ legt fest, wie groß die internen Register sind. Damit wird auch festgelegt, wie viele Bits maximal in einer Operation verarbeitet werden können und wie groß der Adressbereich ist. Bei Intel stieg die Bitbreite schnell von 4 Bit (1971) über 8 Bit (1973) zu 16 Bit (1978) und 32 Bit (1985). Doch dann dauerte es 20 Jahre bis die Architektur auf 64 Bit erweitert wurde. Der Grund war, das vorher der Adressbereich immer um 4 Bits anstieg, was in zwei Chipgenerationen, damals etwa sechs bis sieben Jahren aufgeholt war:

Prozessor Jahr BitBreite Adressbits Standard Speicherchipgröße
4004 1971 4 12 1 KBit
8008 1972 8 14 1 kbit
8080 1974 8 16 4 kbit
8086 1978 16 20 64 Kbit
80286 1982 16 24 256 kbit
80386 1985 32 32 256 kbit

Man brauchte bei Vorstellung der Prozessoren bis zum 80286 immer 128 Chips der aktuellen Generation für den Speichervollausbau Das war viel und angesichts der Preise auch teuer. Beim 80286 stieg dies auf 512 Chips, aber drei Jahre später steigerte der 80286 dies um den Faktor 256, was vier Chipgenerationen entsprach. Erst 2004 war so viel RAM auch bezahlbar und Intel sah die Notwendigkeit, die Bitbreite zu erweitern um mehr RAM zu adressieren. 64 Bit nutzt für die Adressierung übrigens kein Prozessor, es sind zwischen 40 und 48 Bit. 264 Speicherzellen belegen selbst wenn man pro Siliziumatom (Durchmesser 210 pm) ein Elektron speichern könnte, und es keine Isolation zwischen ihnen geben würde, knapp einen Quadratmeter Siliziumfläche. Der durch eine größere Breite der Reigster verfügbare Adressraum dürfte die Hauptantriebsfeder für die Erweiterung der internen Architektur gewesen sein.

Der zweite Grund ist natürlich, dass ein Prozessor maximal so große Zahlen in einem Stück bearbeiten kann wie seine Bitbreite beträgt. Dabei reden wir von Ganzzahlen. Fließkommazahlen mussten frühere Prozessoren per Software verarbeiten bzw. es gab spezialisierte Coprozessoren die dann aber auch Zahlen verarbeiteten die erheblich größer als die Adressbreite des Prozessors war, so z.B. mit dem 8087 Coprozessor bis zu 80 Bit Fließkommazahlen bei einem 16 Bit 8086 Hauptprozessor.

Ganzzahlen braucht man entweder als Indexe für Schleifen oder Arrays, daneben Koordinaten (z.B. auf dem Bildschirm) oder weil der Datentyp, der verarbeitet wird, eine Ganzzahl ist. Das kann auch ein Zeichen sein, das intern auch nur eine Zahl ist. Es ist logisch, das man je größer der Wertebereich ist, man mit mehr Bits immer mehr Anwendungen abdeckt. Mit einem Byte kommt man als Index für ein Feld oder als Koordinate für den Bildschirm nicht weit. Mit 16 Bit kann man schon viele Anwendungen mehr abdecken und mit 32 Bit kann man wirklich Arrays indizieren, die größer als der adressierbare Arbeitsspeicher sind. Der Zugewinn wird also immer kleiner. Für 64 Bit sprechen nur zwei Aspekte. Das eine ist, das nun Fließkommazahlen und Ganzzahlen gleich groß sind , zumindest beim standardisierten Fließkommatyp „long double“, der meist eingesetzt wird. Das vereinfacht die Befehlsstruktur des Prozessors und den Fetch. Der zweite Vorteil ist ,dass man mit so langen Ganzzahlen auch viele Fließkommarechnungen erschlagen kann indem man eine Festpunktdarstellung einsetzt. Für Währungen gibt es z.B. einen 64 Bit Ganzzahl-Typ mit 15 Vor- und 4 Nachkommastellen.

Ansonsten hat aber jede Erweiterung der Bitgröße Nachteile. Ein Befehl besteht aus mehreren Teilen. Zum einen einem Bereich der den Befehlstyp angibt, dann folgen Parameter, die dazu gehören. Bei Rechnungen z.B. die Register die beteiligt sind und spätestens, wenn Daten aus dem Speicher geholt und geschrieben werden müssen, oder es Sprünge gibt folgen dann Adressen. Der Code wird so bei längeren Adressen automatisch länger wenn die Bitbreite ansteigt. Bei der Intel Architektur ist 64 Bit Code rund doppelt so groß wie 32 Bit Code, aber nicht schneller. Deshalb rät selbst Microsoft zu der 32 Bit Version von Office.

Bei einem 32 Bit Prozessor ergibt sich eine sehr elegante Lösung für Befehlsformate, man kommt nämlich mit zwei aus, die dann sogar noch auf geraden 32 Bit Grenzen liegen, dazu später noch etwas. Das wäre das erste Format wenn der Befehl keine Adresse beinhaltet

Opcode Register1/Parameter Register 2 Register 3

8 Bit

8 Bit

8 Bit

8 Bit

Ein Opcode von 8 Bit lässt 256 grundlegende Befehle (ohne Varianten) zu. Für mathematische Operationen im 3-Adressschema stehen dann je 8 Bit für 256 Register zur Verfügung (wenn man noch Teilbereichsoperationen wie 8 oder 16 Bit unterstützen will, kann man hier in jedem Feld 1 Bit opfern und so 8 weitere Unterscheidungsmöglichkeiten haben bei 128 Registern.

Es gibt auch befehle mit weniger Parametern wie keinem (NOP – No Operation) oder EI (Enable Interrupts) oder einem Parameter (bedingte Sprünge, Push/Pop, Incement/decrement …) dann sind hier Felder leer.

Das zweite Format dient dazu Adressen/Zahlen in die Register zu laden/speichern:

Opcode Register1/Parameter Zahl

8 Bit

8 Bit

48 Bit

Das ist dann genau 64 Bit lang, also so lang wie zwei 32 Bit Bündel. Wie man sieht hat man sogar 48 Bit zur Verfügung. Bei Nicht-Adressen würde man davon nur 32 Bits benötigen. Die 48 Bits kann man über segmentierte Adressierung nutzen oder die Adressregister sind 48 Bit breit – solche Architekturen mit unterschiedlich breiten daten- und Adressregistern hatten früher zahlreiche Rechner z.B. alle Rechner von Seymour Cray. Aber auch ohne dieses Feature mit nur 32 Bit für Adressen/Zahlen hat dieses Format am wenigsten „Verschnitt“ (mit einem 16 Bit Format gäbe es außer bei Befehlen ohne Parameter gar keinen Verschnitt, aber ein 32 Bit Rechner greift eben 32 Bit weise auf den Arbeitsspeicher zu. Es ist klar, dass der Verschnitt bei einem 64 Bit Zugriff größer ist.

Natürlich sind auch unterschiedlich lange Befehlsformate denkbar, wie sie bei der x86 Linie historisch gewachsen sind. Aber sie haben deutliche Nachteile. Das Dekodieren ist aufwendiger, der Fetch ist nicht vorhersehbar und vor allem bestraft der Prozessor Zugriffe auf Adressen, die nicht auf ganzen Wortgrenzen liegen. Viele Prozessoren erlauben das gar nicht erst, die Systemprogrammiersprache BCPL sieht z.B. Pointer nur auf durch 4 teilbaren Adressen vor. Bei der x86 Architektur geht der Zugriff weil man dort noch 8 und 16 Bit Werte verarbeiten kann, ein Zugriff auf eine Adresse die zwischen zwei 32 oder 64 Bit Worten liegt erfordert aber dann real zwei Zugriffe, bremst also aus.

Der einzige Nachteil eines 32 Bitters ist eigentlich der auf 4 GByte begrenzte Adressraum, der so heute nicht mehr ausreicht. Aber nur auf den ersten Blick. Zum einen kann man wortweise adressieren, anstatt byteweise, dann sind es schon 16 GByte. Daneben haben wir heute in den Prozessoren mehrere Kerne, jeder kann aber einen getrennten Adressraum haben, anstatt sich einen zu teilen. Das Betriebssystem kann dann in einem gemeinsamen geteilten Bereich sitzen. Bei einem AMD Threadripper mit 64 logischen Kernen käme man so auf 1 TByte Speicher und das ist heute noch mehr, als in den meisten Servern steckt. Wenn 1 TByte in einigen Jahren nicht mehr reichen sollten, wette ich hat auch der Prozessor mehr Kerne.

Daher sehe ich den 32 Bit Prozessor als die Optimallösung an.

3 thoughts on “Die Zahl für heute: 23,12

  1. Man kann mit 32 Bit CPUs viele Problemstellungen lösen, es gibt aber ein paar Sachen wo man die größere Adressbreite braucht.

    1) Solange der Code handgeschrieben und kompiliert ist, reichen 32 Bit Code Adressen oft aus.

    größere Adressen als 32 Bit sind interessant:
    2) bei Interpretern, die einen Teil der Adresse als Tag benutzen, z.B.: für Lisp oder Smalltalk
    3) wenn die Adressen als globale Adressen verwendet werden (keine Unterscheidung zwischen im RAM / auf der Festplatte) z.B. bei IBM AS/400 bzw. iSeries (65Bit Adressen auf dem PowerPC)
    4) bei großen Simulationen, deren Daten nicht in 4GByte RAM passen
    5) bei Videobearbeitung, deren Daten nicht in 4GByte RAM passen
    6) große Datenbanken sind einfacher zu implementieren, wenn man 64Bit Adressraum hat.

    SGI waren mit den MIPS R4000 die ersten, die 64 Bit Adressen verwendet haben.
    Cray und andere Supercomputer habe 64 Bit breite Datenbusse verwendet, aber 24-32 Bit breite Wortadressen.

    MfG

  2. Wenn ich 4K Video bearbeite braucht die Anwendung ab und zu schon über 32 GB. Die Hacks, die Bernd vorschlägt, verschieben das Problem nur um eine Zeit. Mit 64 bit habe wir sicher für 100 Jahre Ruhe, bis man 128 bit braucht.

    1. Das war ja auch der Grund die für SGI die MIPS R4000 CPU zu entwickeln.

      Schade, daß davon nichts mehr geblieben ist (außer im embedded Bereich).

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.