Home Site Map Sonstige Aufsätze Weblog und Gequassel counter

Web Log Teil 405: 28.11.2014 - 1.12.2014

28.11.2014: Funktionelle Einheiten, Vektorinstruktionen und Mehrkernprozessoren

Da wir alle drei Konzepte heute in den Intelprozessoren und auch einigen AMD-Typen finden, mal eine historische Betrachtung was die Unterschiede sind und was wo nützt. Das älteste sind mehrere funktionelle Einheiten. Dazu hole ich erst mal aus und erkläre wie eine CPU allgemein aufgebaut ist. Man kann sie selbst bei einfachen Exemplaren, also z.B. einem 8-Bit Mikroprozessor in mehrere Untereinheiten zerlegen die jeweils eine Aufgabe haben. Da ist zum einen der Befehlsdekoder. Er stellt fest welcher Befehl hinter dem Bitmuster steckt, das gerade geladen wird und welche Register er nutzt. Die Daten selbst bekommt er von einer Einheit die mit dem Speicher kommuniziert, je nach Architektur Load/Store Einheit, Bus-Interface etc. genannt. Dann gibt es noch mindestens eine Ausführungseinheit welche die Befehle auch durchführt, bei einfachen Prozessoren meistens als Arithmetisch-logische Einheit (ALU) bezeichnet. Das ist nur die Grundmenge. Es können durchaus mehrere dieser Einheiten sein.

Schon bald kam man drauf diese Einheiten zumindest zeitweise parallel arbeiten zu lassen. Ein Befehl wird z.B. erst von der Load/Store Einheit aus dem Speicher geholt, durchläuft dann den Befehlsdekoder und dann die Ausführungseinheit. Schon bei der 8086 CPU war die Bus-Interface Unit schon fähig während Decoder und ALU arbeiteten die nächsten Bytes vorrausschauend aus dem Speicher zu laden, das nennt man übrigens Prefetch. Der rapide Geschwindigkeitseinbruch der 8088 CPU kam dadurch zustande, dass neben den Befehlen auch noch Daten von dieser Einheit geholt und geschrieben wurden und durch die halbierte Busbreite kam sie kaum noch dazu den Prefetch durchzuführen, da jeder Transfer nun doppelt so lange dauerte. Auch bei der IBM 7030 Stretch gab es schon Parallelität von Laden/Speichern und ausführen/Dekodieren.

Allerdings brauchen viele Befehle die meisten Taktzyklen beim Ausführen. Das brachte Seymour Cray auf die Idee die Ausführungseinheiten mehrmals vorzusehen. Die CDC 6600 hatte nicht weniger als zehn davon. Sie waren allerdings relativ spezialisiert so gab es eigene Einheiten für Fließkommaoperationen und Ganzzahloperationen und eine Fließkommaeinheit für Addition/Subtraktion, eine für Multiplikation und eine für Division. Der Lohn die theoretische Spitzenleistung war etwa um den Faktor 3 höher als ohne diese mehrfach vorhandenen Einheiten.

Intel führte das beim Pentium ein, der zwei Integerrecheneinheiten hatte (die Fleißkommaeinheit war nur einmal vorhanden). Bei der Ausführung gibt es nun ein Problem, das schon bei der CDC 6600 zuschlug: der Code enthält Instruktionen, die sind unabhängig voneinander und andere bauen aufeinander auf. Also in einer Operation wird ein Ergebnis errechnet und in der nächsten wird mit diesem weiter gerechnet. Das ergibt sich schon daraus das nur eine Rechenoperation pro Befehl möglich ist. Eine einfache quadratische Gleichung wie

Y = x * x + 2 * x + 4

enthält so also vier Operationen von denen immerhin x * x und 2 * x gleichzeitig ablaufen können. Das Addieren aller drei Therme geht aber nicht parallel. Beim Pentium war dies spürbar. Mit bestehendem Code für das Vorgängermodell war er 1,6 mal schneller als dieses. Achtete ein Compiler darauf, Befehle möglichst so anzuordnen das es keine Abhängigkeiten gab, so erreichte er die 2,3-fache Geschwindigkeit. Später integrierte man dies im Prozessor und nannte das Umsortieren der Befehle "Out-of-Order Execution". Ein Haswell-EP Prozessor hat heute vier Integerrecheneinheiten und drei Fließkommarecheneinheiten. Die Zahl ist über 20 Jahre kaum angestiegen, das liegt daran, dass es in Code schwer fällt so viele nicht voneinander abhängige Rechnungen kurz hintereinander zu finden. Mehr Einheiten würden dann nicht ausgelastet werden. Auch Seymour Crays Rechner hatten maximal zwei Einheiten desselben Typs bzw. für dieselben Operanden (er trennte die Register für Ganzzahlen, Fließkommazahlen und Adressen in drei Registersätze).

Historisch als zweites kam dann das Konzept mehrere CPU einzusetzen. Der Unterschied zu mehreren funktionellen Einheiten ist das eine CPU komplett ist, also auch Befehlsdekoder und Anbindung an den Speicher umfasst. Der wesentliche Vorteil einer Mehrkern CPU liegt vor allem bei Multitasking Systemen, die schon in den Sechzigern entwickelt wurden. Dabei liefen mehrere Programme gleichzeitig. oder ein Rechner bediente mehrere Benutzer. Doch auch bei nur einem Programm bringen mehrere CPUs einen Vorteile, wenn große Teile unabhängig von anderen sind. Bei vielen naturwissenschaftlichen Problemstellungen ist dies gegeben. Da wird eine Rechenvorschrift (Programm) mit unterschiedlichen Daten durchgerechnet, die dann ein zwei oder mehrdimensionales Modell ergeben. Dann läuft ein Teil dieser Berechnungen auf einer CPU, der andere auf der anderen. Sie müssen aber miteinander kommunizieren, weil es natürlich am Rand beider Mengen Daten gibt die zwischen den beiden Teilen ausgetauscht werden müssen.

Ein weiteres Problem, das wohl dazu führte das Seymour Cray relativ spät auf diese Karte setzte, ist das der Speicher meist gemeinsam genutzt wird. Dann können sich mehrere CPU gegenseitig blockieren oder zumindest ausbremsen und auch die Bandbreite, also Transferrate wird meist aufgeteilt. Dabei ist ein Grundübel von Speicher, seit er eingeführt wurde das, das er immer langsamer als die CPUs ist.

Der letzte Schritt bei Intel sind Vektorinstruktionen. Bei Crays Rechner waren sie der zweite und das liegt daran dass diese die oben beschriebenen naturwissenschaftlichen Probleme durchrechneten. Wie erwähnt ist die Rechenvorschrift immer dieselbe aber die Daten sind immer andere. Was liegt näher einen Befehl zu entwerfen, der dann nicht zwei Zahlen verarbeitet sondern viel mehr. Bei jedem Takt wird ein Paar verarbeitet bis bei einer Cray 1 maximal 64 Ergebnisse (andere Vektorrechner bis zu 1024!) vorliegen. Der Vorteil ist das die Dekodierung aber auch das Holen der Daten und Scheiben nur einmal erfolgte. Auch die Rechenwerke waren so ausgelegt das sie jeden Takt eine neue Rechnung anstießen, auch wenn diese einige Takte brauchte bis sie das Rechenwerk durchlaufen hatte. Es konnte sogar parallelisiert werden, man musste also nicht warten, bis 64 Werte in den Registern waren sondern konnte nach zwei Werten anfangen während gleichzeitig das Holen der Daten in die anderen Register weiterging.

Bei Intel ist die Konzeption eine andere. In der Form wie Cray sie implementierte bringt sie keinen Performancevorteil, da mittlerweile auch komplexe Rechnungen in einem Takt durchgeführt werden. Stattdessen hat bei AVX, bzw. SSE ein Befehl mehrere Datenworte (Single Instruction, Multiple Data) die in überlangen Registern (bis zu 256 Bit obwohl Fließkommazahlen nur 64 oder 32 Bit breit sind) parallel verarbeitet werden. Anstatt zwei Fließkommazahlen werden so zweimal vier oder zweimal acht auf einaml verarbeitet.

Doch das geht meist nur wenn der Code stimmt. Selbst heute sind Compiler sehr dumm. Das ist ein Ausschnitt aus der Berechnung der Gravitationswirkung von Körpern gegeneinander:

for I:=low(SSystem) to high(SSystem) do
begin
  SSystem[I].Ax:=0;
  SSystem[I].Ay:=0;
  SSystem[I].Az:=0;
  J:=0;
  while J<=high(SSystem) do
  begin
    if I<>J then
    begin
      Dx:=SSystem[I].Pos.Px-SSystem[J].Pos.Px;
      Dy:=SSystem[I].Pos.Py-SSystem[J].Pos.Py;
      Dz:=SSystem[I].Pos.Pz-SSystem[J].Pos.Pz;
      D2:=Sqr(Dx)+Sqr(Dy)+Sqr(Dz);
      D:=Sqrt(D2);
      G:=(SSystem[J].Masse*Grav)/D2;
      SSystem[I].Ax:=SSystem[I].Ax+(G*Dx/D);
      SSystem[I].Ay:=SSystem[I].Ay+(G*Dy/D);
      SSystem[I].Az:=SSystem[I].Az+(G*Dz/D);
    end;
  end;
end;


Dieser Code ist eigentlich super geeignet sowohl für mehrere Funktionseinheiten (unabhängige Berechnung von DX,DY,DZ und AX,AY,AZ). Wie auch mehrere Kerne (so kann man einen Lauf von 1 bis n/2 und einen von n/2+1 bis n aufteilen). Auch Vektoroperationen sind möglich, denn es sind in allen Durchgängen die selben 9 Berechnungen. Compiler sind aber noch heute so dämlich, dass man, damit sie die Instruktionen nutzen, dann jede Berechnung in eine Schleife packen muss also so umschreiben:

      while J<=high(SSystem) do
      Dx[j]:=SSystem[I].Pos.Px-SSystem[J].Pos.Px;
      J:=0;
      while J<=high(SSystem) do
      Dy[j]:=SSystem[I].Pos.Py-SSystem[J].Pos.Py;
      J:=0;
      while J<=high(SSystem) do
      Dz[j]:=SSystem[I].Pos.Pz-SSystem[J].Pos.Pz;


Das bedeutet man muss auch die eigentlich nur kurzzeitig als Zwischenwerte benötigten Variablen DX,DY und DZ in einem Array speichern. Es verwundert nicht, dass von dem Performanceboost von AVX, der bei dem achtfachen der normalen Geschwindigkeit liegen sollte wenig in realen Anwendungen übrig bleibt. Vor allem aber dominieren diese Anwendungen nicht bei den PC-Usern, sie profitieren eher von mehr Kernen, noch mehr aber von mehr Takt da typisch ein Thread sehr viel Performance braucht und andere im Hintergrund mit weniger Leistung auskommen. Daher führte Intel noch vor AVX den Turbo-boost ein. Das ist das "Übertakten" eines Kerns während die anderen wenig zu tun haben. Er nutzt damit das Thermalbudget aller Kerne aus. Wird ein Kern höher getaktet so steigt die Wärmeabgabe meist stark an.

30.11.2014: Der Compiler ist Schuld - oder auch nicht

Ein Compiler hat die Aufgabe eine höhere Programmiersprache in Maschinensprache zu übersetzen. Bei Großrechnern haben kompilierte Programme schon in den Sechzigern die Maschinensprache sukzessive verdrängt. Im PC Bereich war wegen der anfangs langsamen Prozessoren und dem geringen Speicher Assembler noch in den ersten Jahren wichtig, aber heute sicher nicht mehr.

Machen wir einen geschichtlichen Rückblick. Früher hat man auch evaluiert, wie effizient Compiler sind. Die Ergebnisse sind natürlich stark vom Quelltext abhängig. Die NASA hat für die Flugsoftware der Shuttle evaluiert wie schnell HAL als höhere Programmiersprache verglichen mit Assembler war. Und das Ergebnis war damals: 10-20% langsamer. Das Betriebssystem, das zeitkritisch war, wurde daraufhin in Assembler geschrieben, die "Anwendungen" dann in HAL. Ein ähnliches Ergebnis gab es bei Tests der ersten Versionen von Turbo Pascal. Das ist erfreulich, denn das Programmieren ist doch erheblich leichter in einer Hochsprache.

Ich glaube in den letzten Jahren habe ich kaum noch was davon gelesen das jemand in Assembler programmiert, selbst wenn man nur Inline Code verwendet, also Assembler in eine höhere Programmiersprache einbettet. Die ct' hat als SSE eingeführt wurde das "Apfelmännchen" also das Fractal mal in diesen Befehlen programmiert um zu sehen wie viel schneller es ist. Ich habe mal im ct Archiv gesucht: das letzte Mal war 2008. Damals war der C-Compiler meist schneller als der eingefügte Assembler.

Also alles in Ordnung? Nicht unbedingt. Es gibt zwei grundlegende Probleme für den Compiler

Er hat keinerlei Kenntnis, wie der Prozessor intern arbeitet

Das Ausnützen des Befehlssatzes wird immer schwieriger, je mehr Befehle es gibt und desto spezieller sie sind

Er muss heute Befehle erzeugen, die ihre Daten von mehreren Quelltextzeilen bekommen, also den Bereich in dem er optimiert oder den richtigen Befehl sucht auswählen.

Es gibt durchaus Indizien, das hier noch einiges zu verbessern ist. Ich habe schon mal den Test der ct' der Matrixmultiplikation erwähnt. Der Benchmark ist nun extrem popelig, denn er besteht aus einer Zeile:

c[i][j] += a[i][k] * b[k][j];

Diese wird in drei Schleifen (i,J,k) für Spalten und Zeilen der beiden Matrizen durchlaufen. Nun gibt es einen Befehl, der genau diese Zeile durchführt: FMA - fused Add Multiply. Dieser Befehl kann sogar vier doppelt genaue und acht einfach genaue Zahlen in einem Takt addieren und multiplizieren. Das bedeutet pro Takt sollte er acht bzw. 16 Fließkommaoperationen durchführen, multipliziert man dies mit dem Takt und den acht Threads des Core i7- 4750HQ  Prozessors so sind dies 180 GFlops bei 4 Kernen, Hyperthreading sollte weitere 30% erreichen.

 Das Ergebnis: Microsofts C-Compiler erreicht 3,2 GFlops, wenn man AVX explizit nutzen will steigert sich das auf 6,5 GFlops - er nutzt aber nur einen Kern. Mit einer Erweiterung die Autoparallisierung nutzt klettert das dann auf 37 GFLOPS. Trotzdem - nur ein Viertel der theoretischen Leistung. Es zeigte sich auch, dass die Ergebnisse extrem sensitiv waren wie der Code aufgebaut war. Führte man die Initialisierung von C in einer vorgezogenen Schleife durch so war die Geschwindigkeit in der Rechenschleife eine andere als wenn dies in der Rechenschleife direkt erfolgte.

Intel als Prozessorhersteller sollte bessere Compiler liefern und sie sind auch besser. Sie kommen 5-8 GFlops bei einem Kern, auf 20 GFLOPS mit AVX Unterstützung und Nutzung aller Kerne, nimmt man die Erweiterung OpenMP hinzu kommt man auf 37 GFLOPS - ein vielfaches der Basisleistung. Doch programmiert man das nicht selbst sondern nutzt eine Bibliotheksfunktion, von der man annehmen kann, dass sie optimal programmiert ist, wahrscheinlich in Assembler, so erreicht man 140 GFLOPs.

Das bedeutet man kann ein und dasselbe Programm extrem beschleunigen und das, obwohl es sich nur um eine Programmzeile handelt. Es dürfte jedem klar sein, dass es nicht für jedes Problem eine Bibliotheksfunktion gibt, das Ergebnis ist also ernüchternd.

Es gibt noch ein zweites Faktum: das waren gescheiterte Prozessoren. 1989 lancierte Intel den i860 Prozessor, genannt "Cray on a Chip", weil er die Leistung einer Cray 1 erreichte. Allerdings erzeugten Compiler Programme die nur ein Achtel der theoretischen Pearkperformance erreichten. Das lag nicht nur an den Compilern, der Chip galt als schwer zu programmieren und auch Assembler erreichte nur die Hälfte der Maximalleistung, aber immerhin noch viermal schneller als der Compiler.

Das zweite war das Scheitern des Itanienporzessors. Der hatte eine Philosophie die man VLIW nennt - very Long instruction Word. Dieser Ansatz soll zum einen die Abhängigkeit vom dem Speichersystem reduzieren, dass viel langsamer als der Prozessor ist - man überträgt nicht einen Befehl sondern mehrere, die man zu einem Superwort zusammenfasst. Das Konzept gibt es auch bei Signalverarbeitungsprozessoren und die Cyber Star setzte es auch ein. Das zweite ist das heute alle Prozessoren nicht nur mehrere Kerne haben, sondern jeder Kern auch mehrere Recheneinheiten. Während die Kerne aber nach außen hin sichtbar sind, sind es die Funktionseinheiten nicht. Die Prozessoren versuchen mit einigen Tricks diese Einheiten alle auszulasten sogar indem sie die Befehlsreihenfolge verändern. Der Itanium wollte diese aufwendige Logik einsparen und delegierte die Aufgabe an den Compiler. Einige Bits im Superwort enthielten die Informationen welche Befehle parallel ablaufen konnten und welche nicht. So richtig gelang dies nicht, denn die Performance war immer nur mittelmäßig. Folgerichtig stellte Intel auch diese Entwicklung ein.

Die Chancen, dass ein Compiler optimalen Code erzeugt sind um so besser, je weniger Befehle ein Prozessor hat. Untersuchungen von Kompiliertem Code bei den damals üblichen CISC Prozessoren ergab, dass vom gesamten Befehlssatz nur ein Bruchteil sehr häufig benutzt wurde, manche Befehle niemals. Das war damals der Startschuss für RISC - ein kleinerer Befehlssatz sollte schneller ausgeführt werden. Heute arbeiten die x86 und x64 Prozessoren intern auch mit RISC, sie machen einen Zwischenschritt und übersetzen den CISC Code in RISC. das zeigt schon die Problematik. Trotzdem führte Intel mit MMX, SSE 1-4 und AVX 1+2 immer neue Befehle ein, das mit wurde der Befehlssatz noch komplizierter und die Wahrscheinlichkeit, das der Compiler die beste Lösung findet schwindet, zumal er ja nur eine Vorschrift anwendet.

Was ist die Lösung? Nun OpenMP zeigt eine Lösung auf. Bei OpenMP, das bei Microsofts und Intels Compiler die Performance deutlich erhöhte, gibt der Programmierer an was parelellisierbar ist. Offenbar sind diese immer noch schlauer als die Compiler. Das zeigte auch der Code denn mein Compiler erzeugte. Dort wurde ich an eine Optimierung der Core 2 Architektur erinnert. Es handelt sich um den Befehl "Speichere Daten an Adresse X" gefolgt wenige Befehle später von "Lade Daten von Adresse X". Für einen Programmierer erscheint diese Sequenz dämlich, denn erst speichere ich einen Wert dann hole ich ihn, stattdessen kann ich ihn gleich in einem Register halten, das ist viel schneller. Obwohl nur vier der acht FPU Register genutzt wurden, fand sich genau diese Kombination im Code in dieser Zeile. Bei der Core 2 Mikroarchitektur hat man diese das Umsortieren der Befehle aufhaltende Kombination erkannt und sie blockiert nicht mehr das Umsortieren.

Das zeigt: Compiler sind heute noch strunz doof. Der Compiler hätte eigentlich so viel wie möglich in den Registern halten müssen denn der Speicherzugriff ist auch noch langsam. Was folgt daraus? Meine persönliche Ansicht nach sollte man das ursprüngliche Ziel von RISC beibehalten. Anstatt das Innenleben der CPU umzumodeln, damit sie das beste aus dem Code herauskitzeln, indem sie spekulative Sprünge vorhersagen, Register umbenennen, Befehle umsortieren oder Zugriffsmuster auf den Speicher vorhersagen sollte man wenige Befehle sehr schnell ausführen und das geht zum einen mit RISC zum andern mit Eingehen auf den Compiler. Beispielsweise übergeben die meisten Compiler Parameter an Funktionen über den Stack - nicht nur fehlerträchtig, sondern auch langsam.

Stattdessen sollte man für jede Routine eigene Register für die Parameterübergabe zur Verfügung stellen, die man als Fenster aus einem größeren Registerfile auswählt. Dieses Konzept des Registerfiles kennen einige RISC Architekturen. Anders als beim Cache kann der Compiler so viel besser kontrollieren wo die Daten gespeichert werden. Das gilt auch für das Laden von Routinen. Der Compiler weriß wie groß ein Unterprogramm ist und könnte es vorrausschauend in den Cache laden lassen. Auch das Konzept des VLIW ist nicht so dumm. Zumindest wäre es möglich in einem Statusbyte oder einigen Bits pro Befehl anzugeben welche Einheit man nutzt. Gleiche Werte erlauben es dem Prozessor zu erkennen: okay diese Befehle hängen voneinander ab. Man kann mit einem Befehl eine Einheit für eine Befehlssequenz reservieren und wieder freigeben.

Das sind nur einige Ideen, aber je mehr ich über Mikroarchitekturen schreibe, derzeit bin ich bei Nehalem, also der drittletzten Architektur die 2008n erschien, desto mehr wird mir klar, dass man eine ganze Menge Aufwand betreibt um im Prozessor die Performance zu steigern, ohne das der Anwender das heute groß mit dem Compiler beeinflussen könnte. Dabei kennt der den Quelltext er kennt Zugriffsmuster und er weiß wo Routinen sind die häufig gebraucht werden und welche wo langsam sind, bzw., welche als nächstes dran kommen (die man schon mal vom Speicher in den Cache geladen werden können). Man sollte also die Zusammenarbeit intensivieren anstatt neue CISD Befehle zu kreieren.

Es gäbe sogar eine noch weitergehende Möglichkeit: die des Profilings. Das gibt es heute als Tools für Entwicklungsstudios, aber es wurde auch bei PC's eingesetzt die auf dem Alpha Prozessor basierten und für x86 geschriebene Programme als Emulation ausführten: Ein Programm wird zuerst einfach in Maschinensprache übersetzt und vom Anwender normal benutzt. Dabei führt ein mitgeliefertes Modul Buch wie oft welche Routine ausgeführt wird, auf welche Variablen zugegriffen wird etc. Das kann man ausbauen und auch zeitliche Zusammenhänge erkennen (eine Routine wird kurzzeitig sejr oft aufgerufen, dann wieder längere Zeit gar nicht). In einem zweiten Schritt wird das Programm neu übersetzt wobei die Informationen genutzt werden es zu optimieren. Ohne spezielle SIMD Befehle z.B. Variablen in Registern dauerhaft zu halten oder zumindest im Cache, Routinen vorbeugend laden wenn man weiß das sie oft nach anderen aufgerufen werden. Man kann es aber auch nutzen um CISC Befehle wie obigen FMA zu nehmen indem man beim  lauf das Zugriffsmuster und die Operationen in einem Array erkennt.

1.12.2014: Dhrystone-MIPS und Transistorcount

Auch heute wieder ein Computerthema auf das ich beim Schreiben gekommen bin, demnach hat nach meinen Recherchen ein Rechner in der Penryn Architektur (Core 2) Geschwindigkeit von bis zu 18.000 MIPS, also Millionen Instruktionen pro Sekunde. Das sind pro Kern (wahrscheinlich auf ein 4-Kernsystem bezogen) bei 3,2 GHz Takt 1,4 Instruktionen pro Sekunde, und auch in der ct' hat man bei einer neuen Architektur namens VISC 5,24 DMIPS/Takt, allerdings wahrscheinlich auf zwei Kerne bezogen.

Nun was sind MIPS - es ist die Abkürzung von Millionen Instruktionen pro Sekunde. Das scheint ein einfaches Kriterium zu sein, ist es aber nicht. Zum einen ist es schwer Instruktionen zu zählen. Natürlich kann man ein Benchmark erstellen, denn Assemblerquelltext ansehen und dann vor allem bei Schleifen die Instruktionen zählen, mit den Durchläufen multiplizieren. Doch jenseits künstlicher kleiner Inhalte, bei konkreten größeren Problemen wird das schwer.

Das zweite ist das man ja damit auch Prozessoren und Rechner vergleichen will, und dann wird es schwer weil die Instruktionen verschiedener CPU unterschiedlich mächtig sind. Das sieht man schon bei der x86 Architektur: Eine Instruktion ist eine einfache Register-Register Bewegung von 32 Bit Daten wie "MOV EAX,EBX" aber auch die kombinierte Addition und Multiplikation von acht Einfach genauen Fließkommazahlen (FMA).

Erst recht wird es problematisch Prozessoren mit unterschiedlichen Architekturen zu vergleichen: im Extremfall braucht ein 8 Bit Prozessor für eine 64 Bit Addition acht Instruktionen, ein 64 Bit Prozessor aber nur 8. Aus diesem Grunde sind die MIPS Angaben die man liest daher meist keine "echten" MIPS also Millionen Anweisungen in der konkreten Architektur, sondern Dhrystone MIPS. Das ist ein Benchmark ursprünglich in ADA, heute in C. Als 1 MIPS wurde die Geschwindigkeit einer VAX 11/780 definiert, die 1757 Dhrystone erreichte. Durch diesen Wert musste man die Dhrystone Ergebnisse teilen um die DMIPS (Dhrystone  Millionen Instruktionen pro Sekunde) anzugeben. Diese Festlegung erfolgte nicht weil die VAX 11/780 eine Million Instruktionen pro Sekunde ausführen konnte - das war als typischer CISC Rechner bei einem Takt von 5 MHz gar nicht möglich, sondern weil sie in Problemstellungen ungefähr so schnell wie eine IBM 370/158-8 war, die man als eine Maschine Ansah die 1 MIPS pro Sekunde ausführte. (Real waren es etwa 0,5 MIPS bei der VAX 11/780)

Ein Prozessor der nicht so mächtig war, erreichte daher weniger DMIPS bei einem Takt als ein leistungsfähiger Prozessor. 8 Bit Prozessoren erreichten beim gleichen Takt wie VAX weniger als 0,1 DMIPS. Mit echten MIPS hat das nichts zu tun, man kann nur sagen, dass mein Haswell Prozessor bei 3,8 GHZ mit etwas über 16.000 DMIPS 16.000-mal schneller in diesem Benchmark als eine VAX 11/780 ist.

Eine Cray 1A erreicht im aktuellen Dhrystone Benchmark 2.1 im optimierten Falle 13888 Dhrystone, die VAX 11/780 1662. Er ist also nach diesem Benchmark nur rund achtmal schneller, aber neunzigmal teurer - ein schlechter Deal oder? Nein, denn das zeigt das Problem jedes Benchmarks. Dhrystone misst nur die Ganzzahlperformance und Fließkommaleistung. Die war bei den Cray aber schlecht. Eine Cray 1A erreichte maximal (als Peak) 40 (echte) MIPS, aber 160 MFLOPS. Bei Fließkommarechnungen war sie wegen ihrer darauf ausgelegten Architektur 400-mal schneller als eine VAX.

Das leitet mich zum zweiten Kriterium über wie man Rechner vergleichen kann: dem Transistorcount. Er ist allerdings genauso wenig verlässlich wie der Dhrystone Benchmark. Wenn man davon ausgeht, dass man für die Erledigung einer Aufgabe, z.B. dem Aufbau eines 64 Bit Rechenwerkes eine bestimmte Anzahl von Gattern braucht, kann man davon ausgehen, dass Rechner mit ähnlich vielen Transistoren etwa gleich schnell sein sollten. Das Kriterium ist aber ebenfalls dehnbar. Zum einen kann man mit einer effizienten Architektur Transistoren einsparen. So brauchen RISC Rechner meist weniger Schaltelemente als CISD Rechner. Des weiteren kann man mit Caches sehr viele Transistoren verbrauchen, ohne dass die Rechenleistung dadurch proportional zum Verbau in der Logik ansteigt. Bei der ICore Architektur steckten von 291 Millionen Transistoren nur 19 Millionen in den Rechenwerken.

Aber immerhin: eine CDC 6600 aus dem Jahre 1965 hatte 400.000 Transistoren und leistete 3-5 MIPS (echte MIPS). Ein Intel 80386 aus dem Jahr 1985 hatte mit 375.000 Transistoren in etwa gleich viel und erreichte auch um die 4-6 MIPS (diesmal aber DMIPS). Doch bei Fließkommarechnungen war sie zehnmal schneller, da sie diese nativ durchführte, während der 386 dazu einen Coprozessor brauchte.

Wie viele Transistoren eine Cray 1 hat ist nicht bekannt, aber man kann es abschätzen. Ich habe mal die Angabe 1 Million Transistoren gefunden, auch 200.000 Chips, das passt nicht zusammen. Rund 73.000 der Chips waren Speicherbausteine. Bleiben 130.000 Chips für die Logik. Bei 4-5 Gattern pro Chip, eine schon damals niedrige Integrationsdichte kommt man auf etwas mehr als 1 Million Transistoren. Für ein einfaches Gatter braucht man zwei Transistoren, es können aber auch deutlich mehr sein. Doch mindestens 1,3 Millionen Transistoren sind es auch in diesem Fall. Damit wäre eine Cray 1A vergleichbar mit einem 486 DX mit 1,2 Millionen Transistoren. Bei Integer Anweisungen stimmt das auch: 13.8888 DMIPS vs 15.000 DMIPS bei 33 MHz. Doch bei Fließkommaanweisungen braucht man einen 100 MHz DX/4 mit 50 DMIPS um 12,1 MFLOPS zu erreichen. Das schafft eine Cray 1A schon bei Skalraoperationen. Bei Vektoroperationen zieht sie mit 98 MFLOPS beim Whetstone Benchmark davon.

Aber wir haben einen Trost: Während auf einem 486 mit 100 MHz noch Windows 95B problemlos ausführen kann, wäre eine Cray 1 mit 14 DMIPS sowohl zu langsam. wie auch der Arbeitsspeicher von 8 Megabyte zu klein (falls es eine Windows 95 Version für die Cray gäbe). Ernüchternd nicht? Also mit der Cray 1 konnte man das Wetter vorhersagen, Supernovaexplosionen simulieren, aber es reicht nicht für ein nicht gerade anspruchsvolles grafisches Betriebssystem.

Für Windows 8 werden als Systemanforderungen ein 1 GHz Prozessor und 1 (32 Bit) bzw. 2 GByte RAM (64 Bit genannt). Geht man von 2 DMIPS pro Takt aus, sind das 2000 DMIPS. Eine Cray 3 hätte vier Prozessoren gebraucht um diese Leistung zu erreichen. Eine 4-Prozessor Cray 3 hatte auch zwischen 2 und 8 GByte RAM. Das passt also. Kurzum: 2014 hat ein Betriebssystem als Minimalanforderung an die Hardware die Leistung des schnellsten Rechners von 1995 (und man kann davon ausgehen dass bei einer Cray 3 das Betriebssystem noch etwas Leistung zum Rechnen übrig lies...).


Sitemap Kontakt Neues Impressum / Datenschutz Hier werben / Your advertisment here Buchshop Bücher vom Autor Top 99