Home Computer Programmiersprachen Site Map counter

Computer wo ist deine Leistung?

Einleitung

Es ist heute nicht mehr üblich die Rechenleistung in Megaflops anzugeben, also Millionen Fliesskomma Instruktionen pro Sekunde oder wenn von Integer Zahlen die Rede ist von MIPS - Millionen Instruktionen pro Sekunde. Stattdessen werden künstliche Benchmarks durchlaufen die entweder gängige PC Anwendungen abdecken oder wissenschaftliche Anwendungen durchrechnen wie die SPEC Suite.

Tauchen ab und an MIPS oder MFlops Angaben auf, so gibt es dumme Menschen wie mich die diese nachrechnen indem Sie ein kleines Programm schreibt welches einfach 100 Millionen mal x:=x+0.000345666; oder etwas ähnliches rechnet. Hier stieß ich schon vor Jahren auf ziemliche Diskrepanzen zu den Werten die man "offiziell" fand und den eigenen gemessenen Werten. So gab Intel für den ASCII-White - einen Rechner aus 9000 Pentium-Pro Prozessoren mit 180 MHz 1800 GFlops an, also 200 MFlops pro Prozessor. Mein 350 AMD MHz K6-2 schaffte aber nur ca. 30. Nach dem Benchmark "Sandra" von Sissoft sollte er aber 800 MIPS und 250 MFlops leisten. Zu dem folgenden Artikel gibt es das Programm als Source und Binary auch zum herunterladen.

Die Suche nach dem Fehler

Das erste Benchmark programmierte ich in Turbo Pascal 7.0 es umfasste die einfache Prozedur:
BEGIN
  X:=0.0; Y:=9.9; A:=Timer;
  FOR J:=1 TO Scale DO
  FOR I:=1 TO 10000 DO X:=X+(Y*Y-Y) / Y;
  Tr:=(Timer-A); A:=Timer;
  FOR J:=1 TO Scale DO
  FOR I:=1 TO 10000 DO ; {Leerschleife zur reinen Berechnung der Fliesskommarechnung}
  A:=(Timer-A);
  MFlops:=(Scale*0.04)/(Tr-A);
END;

Ich habe damals mit einem AMD K6-2 mit 350 MHz gearbeitet. Unter Turbo Pascal 7.0 bekam ich so ca. 27-29 MFlops. Nun passt zwar dieses Benchmark vollständig in den L1 Cache, allerdings nutzt es nur eine FPU, da der X Wert innerhalb der Berechnung verwendet wird. Da man mit Turbo Pascal nur max. 80286/7 Code erzeugen kann und selbst 30 MFlops für meine Zwecke reichten, beließ ich es dabei.

Doch seltsam - unter Delphi 4 erreicht eine Konsolenanwendung mit 32 Bit Code keine bessere Werte - doch vielleicht liegt es eben einfach an Pascal, schlussendlich kann man bei Delphi weder trickreiche Optimierungen einstellen noch die Codeplattform (386,486,P-Pro etc....) angeben. Mit dem Beginn des Softwarestudiums hält nun C Einzug und in C kann man all das Einstellen - und erhält auch bessere Werte, ca. 35-40 MFlops, mehr aber nicht so viel wie zu erwarten wäre. Beim Untersuchen des Assemblercodes stellte ich fest, das der Borland-C Compiler intelligenter die Integerbefehle für die Laufschleife unter den Code mischt und den Befehl "Wait" weglässt, der nach den FPU Anweisungen bei Pascal steht und den Coprozessor früher mit dem Prozessor synchronisierte, als diese noch getrennte Bausteine waren. Wie Pascal aber benutzt der C-Compiler nur ein Fliesskommaregister zur Speicherung von Werten.

Die Suche nach der Optimierung

Damit war klar, dass man zwar mehr erreichen kann doch noch nicht wie. Ich schrieb eine Delphi Prozedur die anders als hier getrennte Variablen verwendete wie:

a:=c+b;
d:=e+f;

Athlon CPU DieDiese sollten getrennt voneinander berechnet werden und somit beide FPU's eines K6-2 ausnutzen. Doch dem war nicht so. Ich wand mich mit dem Problem an Arne Schäpers der in der ct eine Delphi Corner hat. Er sagte mir das in der Schleife nur 11 FPU Befehle wären, da wäre nichts mit Parallelisieren drin, fügte aber Integer Anweisungen ein die dann praktisch keine Rechenzeit benötigten. Noch seltsamer war allerdings, das sein P-III 550 MHz genauso lang für die Schleife brauchte wie mein AMD-K6/2, also lag es nicht nur am Prozessor.

Nun hatte ich Blut geleckt: Irgendwie musste man doch an höhere Werte kommen. Ich besann mich auf zwei Strategien: Erstens bei Programm zu schreiben welches nach und nach immer mehr Befehle in eine Schleife packt und dabei alle Datentypen (Word, Longint, Int64, Single, Double, Extended und die Kombination) durchrechnet und die Zeiten anzeigt und mich gleichzeitig im Technical Manual von AMD und Intel schlau zu machen.

In letzterem konnte ich bald erste Probleme erkennen. Bis 7 Byte/Befehl kann der K6-2 in einem Takt dekodieren. Bei mehr braucht er länger. Nun sind aber alleine die Daten von Double Variablen 8 Byte lang und bei Extended schon 10 Bytes. Obgleich die FPU intern immer mit Extended also 80 Bits rechnet sollten so Single - Double -Extended Werte länger brauchen. Um es vorweg zu nehmen - das ist in den Benchmarks deutlich zu sehen.

Das zweite war die FPU Pipeline. Sie hat eine Latency von 2 Takten und einen Ausstoß alle 2 Takte, d.h. das jeder Befehl also schon mindestens 2 Takte braucht. Dazu kommt die Ausführdauer für den Befehl und die liegt bei den Befehlen FADD [Mem] bei 2 Takten, während bei FADD STn nur ein Takt gebraucht wird, d.h. wenn man die FPU Register benutzt halbiert sich die Befehlsdauer. Nun generierten aber alle Compiler Code der Form FADD ptr [EBX+nn], es ist klar das dieser länger sein muss als die Referenz auf die Fliesskommaregister. Damit war eine Fehlerquelle schon gefunden.

Benchmarks mit 5 Compilern

Nun schrieb ich den Benchmark den Sie auch hier herunterladen können. Das Grundprinzip:

Basis ist eine Prozedur mit folgendem Aufbau:

function standard: double;

const flies_erhoehen: double = 1 / 12345678; {sollte eine kleine zahl sein}
      max_durchlauf  = 100 * 1000 * 1000;

begin
  a:= 1000;
  start:= timer;
  for i:= 1 to max_durchlauf do
  begin
    a:= a + flies_erhoehen;
  end;
  standard:= timer - start;
end;

Diese Prozedur macht also nichts anderes als 100 Millionen Fliesskomma Additionen. Von dem Zeitwert wurde dann noch die einer anderen Prozedur ermittelte Leerlaufzeit der Schleife abgezogen um insbesondere die 16 Bit Compiler nicht zu benachteiligen, weil 100 Millionen ein 32 Bit Integer Wert ist.

Nun fügte ich in weiteren Funktionen sukzessive nach der roten Anweisung weitere einfache und nicht von einander abhängige Rechnungen ein. Jede Funktion liefert einen Zeitwert zurück. Macht man dies nun nicht nur mit Double Daten sondern auch Single, Extended oder verschiedenen Int Typen wie Word, Longint und Int64, so erhält man eine Tabelle mit Rechenwerten.

Dieses Benchmark habe ich dann durch die Compiler Free Pascal 1.02, Virtual Pascal 2.1 Turbo Pascal 5.5 / 7.0 und Delphi 4/5 gejagt. Ein Benchmark von Delphi 1 liegt auch vor, aber seine Werte liegen jenseits von Gut und Böse. Bis auf Virtual Pascal und Delphi 4/5 ging es die Benchmark unter DOS zu fahren. Free Pascal und Delphi 4/5 erfordern ein Win 32 DOS, welches man nur unter Win 9x/NT in einer DOS Box hat. In diesem Falle habe ich alle Programme die sonst liefen beendet. Das Programm ist durch bedingte Anweisungen so gehalten dass es jeder der 6 Compiler übersetzen kann.

Die Ergebnisse: (AMD K6-2 350 MHz)

Turbo Pascal 5.5

Anzahl 1 2 3 4 5 6
----------------------------------------------------------------------------
Double 6.26 100% 11.37 182% 17.96 287% 22.79 364% 27.63 441% 34.21 546%
Single 6.27 100% 11.36 181% 16.81 268% 22.25 355% 30.48 486% 33.06 527%
Extend 16.81 100% 33.07 197% 48.16 286% 64.43 383% 80.69 480% -65439.06 -389286%
Longint 3.40 100% 6.82 201% 10.81 318% 13.68 402% 17.08 502% 20.54 604%
Comp 12.25 100% 23.62 193% 36.47 298% 49.05 400% 61.02 498% 72.67 593%
Word 0.87 100% 1.10 126% 1.98 228% 2.25 259% 3.13 360% 3.41 392%
Mix D/L 9.94 100% 20.54 207% 30.76 309% 40.48 407% 50.47 508% 62.12 625%

Turbo Pascal 7.0

Anzahl 1 2 3 4 5 6
----------------------------------------------------------------------------
Double 5.38 100% 10.81 201% 19.06 354% 21.64 402% 27.07 503% 32.46 603%
Single 5.44 100% 10.76 198% 16.25 299% 21.64 398% 27.90 513% 32.51 598%
Extend 16.20 100% 32.51 201% 47.57 294% 64.42 398% 80.08 494% 96.39 595%
Longint 1.98 100% 3.67 185% 4.50 227% 6.26 316% 7.96 402% 9.94 502%
Comp 11.64 100% 23.12 199% 35.87 308% 48.16 414% 60.42 519% 72.11 620%
Word 0.55 100% 1.42 258% 1.65 300% 2.57 467% 3.08 560% 3.40 618%
Mix D/L 7.08 100% 15.93 225% 23.67 334% 29.60 418% 37.07 524% 47.89 676%

Delphi 4 (Win 32)

Anzahl 1 2 3 4 5 6
----------------------------------------------------------------------------
Double 4.17 100% 5.33 128% 8.62 207% 10.88 261% 14.17 340% 16.53 396%
Single 3.29 100% 5.33 162% 8.57 260% 11.26 342% 14.17 431% 20.32 618%
Extend 11.81 100% 24.17 205% 35.92 304% 47.18 399% 59.48 504% 76.18 645%
Longint 0.11 100% 0.06 55% 0.38 345% 1.65 1500% 0.99 900% 1.31 1191%
Comp 2.37 100% 4.88 206% 8.90 376% 10.33 436% 18.01 760% 19.88 839%
Word 0.55 100% 0.38 69% 0.66 120% 2.69 489% 2.69 489% 3.68 669%
Mix D/L 4.61 100% 11.21 243% 13.67 297% 15.65 339% 24.99 542% 26.92 584%

Virtual Pascal 2.1 (win 32)

Anzahl 1 2 3 4 5 6
----------------------------------------------------------------------------
Double 3.62 100% 5.06 140% 6.53 180% 8.62 238% 10.66 294% 12.74 352%
Single 3.30 100% 3.89 118% 6.21 188% 8.35 253% 9.17 278% 10.98 333%
Extend 10.66 100% 21.86 205% 33.12 311% 43.11 404% 54.98 516% 67.62 634%
Longint 0.33 100% 0.93 282% 1.81 548% 2.75 833% 3.24 982% 4.61 1397%
Comp 10.93 100% 18.89 173% 33.01 302% 41.25 377% 52.01 476% 61.63 564%
Word 0.38 100% 1.98 521% 4.61 1213% 5.71 1503% 7.03 1850% 8.85 2329%
Mix D/L 4.17 100% 7.86 188% 11.37 273% 13.34 320% 20.27 486% 15.65 375%

FPK Dos Version

Anzahl 1 2 3 4 5 6
----------------------------------------------------------------------------
Double 1.76 100% 4.50 256% 6.20 352% 7.58 431% 9.89 562% 11.92 677%
Single 1.75 100% 4.18 239% 5.93 339% 7.58 433% 9.33 533% 11.04 631%
Extend 4.45 100% 15.32 344% 27.91 627% 36.96 831% 46.14 1037% 55.25 1242%
Longint 0.44 100% 1.65 375% 1.92 436% 3.29 748% 3.35 761% 4.17 948%
Comp 1.65 100% 5.33 323% 7.63 462% 10.16 616% 11.59 702% 15.05 912%
Word 1.92 100% 3.30 172% 3.34 174% 4.78 249% 5.60 292% 6.76 352%
Mix D/L 2.74 100% 7.91 289% 12.47 455% 14.72 537% 15.60 569% 17.30 631%

FPK Windows Version

Anzahl 1 2 3 4 5 6
----------------------------------------------------------------------------
Double 1.53 100% 4.39 287% 7.84 512% 9.66 631% 10.32 675% 12.30 804%
Single 1.26 100% 3.72 295% 5.88 467% 7.73 613% 9.44 749% 11.48 911%
Extend 4.00 100% 14.88 372% 27.51 688% 36.68 917% 45.85 1146% 55.03 1376%
Longint 0.16 100% 1.09 681% 1.42 887% 2.03 1269% 2.25 1406% 2.85 1781%
Comp 1.09 100% 4.33 397% 6.59 605% 9.27 850% 12.08 1108% 15.37 1410%
Word 1.15 100% 2.41 210% 3.29 286% 4.44 386% 5.87 510% 5.93 516%
Mix D/L 2.68 100% 7.69 287% 8.83 329% 10.65 397% 15.65 584% 17.35 647%

Sieht man sich diese Tabelle ein so ist klar: es gibt schnelle und langsame Compiler. Deutlich ist das die 16 Bit Systeme Turbo 5.5 und 7.0 nicht mit dem Rest mithalten können - außer es handelt sich um den Word Benchmark, bei dem sie die schnellsten sind - klar dafür reicht 16 Bit Code. Bei 32 Bit Systemen ist dieser Wert immer langsamer als der Longint Wert, da rückkonvertiert werden muss.

Deutlich sichtbar ist auch der Einfluss der Datenlänge. Obgleich die FPU intern immer mit 80 Bit also Extended rechnet sind diese Rechnungen um Größenordnung langsamer als Double oder Single. Das der Rechner 2 FPU's und 2 Integer Alu's hat sieht man wenn der Anstieg innerhalb einer Reihe langsam ist oder gar gleich bleibt. Manchmal gibt es auch Kuriositäten wie die roten Zahlenpaare. Hier hat eine Prozedur mit 5 Rechnungen weniger Zeit als eine mit 4 gebraucht - offensichtlich ein günstiges Zusammenkommen von Auslastungen, d.h. Waits.

Es gibt weiterhin eine Rangfolge in der Form: 16 Bit Turbo < Delphi < Vpascal/Fpk. Dies entspricht auch in dem erzeugten Code. Nur bei letzterem sind Einstellungen für moderne Prozessoren möglich, nur letztere fügen keinen Wait am Ende der Flieskommaschleife ein.

Das FPK noch ein gutes Stück schneller als Vpascal ist kann man an den optional erstellten Assembler Leistung sehen. FPK benutzt wenigstens ein FPU Register (ST1) für die Zwischenwerte, allerdings eben nur eines. So ist auch zu erklären das FPK bei einem Durchlauf nahezu doppelt so schnell wie Vpascal ist, diese 1.5 Sekunden Vorsprung aber dann konstant bleiben und nicht besser werden, d.h. bei mehr als einer Fliesskommazahl in der Schleife (Spalte 2-6) wird FPK rapide langsamer und ist bei 6 Werten nicht mehr besser als VP.

Auch bei der ALU sieht man den Effekt deutlich. Der K6-2 hat zwei ALU's. Eine ist belegt durch die Schleife, die zweite frei. So braucht der erste Longint Wert deutlich weniger Zeit als der zweite (Bei Turbo entsprechend der 16 Bit Word Wert). Auch in der Folge sind dann Sprünge von 3->4 und 5->6 deutlich. Das Mixen von Fliesskomma und Integer Operationen ist in diesem Beispiel (Mix D/L) wenig effektiv, weil diese immer erst aus dem Speicher geholt werden müssen mangels der kleinen Registerzahl der x86 Architektur. Verwendet man Konstanten so lässt sich allerdings ein bedeutender Performancegewinn erzielen wie ein probehalber durchgeführter Benchmark mit Konstanten anstatt Variablen zeigt:

Vpascal

Anzahl 1 2 3 4 5 6
----------------------------------------------------------------------------
Double 3.23 100% 3.78 117% 9.50 294% 8.34 258% 13.56 420% 13.51 418%
Single 3.23 100% 4.06 126% 5.32 165% 8.95 277% 10.92 338% 12.96 401%
Extend 3.18 100% 12.68 399% 24.43 768% 33.61 1057% 45.97 1446% 50.25 1580%
Longint 3.18 100% 3.23 102% 4.88 153% 3.79 119% 4.88 153% 5.82 183%
Comp 3.18 100% 10.87 342% 19.98 628% 29.38 924% 37.67 1185% 47.56 1496%
Word 3.29 100% 3.29 100% 3.90 119% 5.32 162% 6.47 197% 7.41 225%
Mix D/L 3.29 100% 4.72 143% 9.11 277% 14.44 439% 14.44 439% 16.52 502%

In der letzten Reihe wurden beide Benchmarks (Double und Longint) kombiniert wobei auf jede Fliesskomma Anweisung eine Integer Anweisung folgte. Dadurch sollte der Code gut parallisierbar die Integer und Fliesskomma Alu's ausnutzen. Tut er das nicht so wäre der Mix doppelt so lang wie die Einzelwerte, doch dem ist nicht so, etwa 50 % der Integerzeit wird eingespart, weil nun beide Einheiten ausgelastet sind. Bei 3 Rechnungen ergibt sich sogar der günstige Effekt, das der kombinierte Wert schneller ist als jeder Einzelwert!

Und nun richtig fette Daten...

Ab Mitte 2001 verfügte ich über einen AMD 1200 Athlon. Nun hat dieser anders als der K6-2 keine Rechenschwäche bei Fließkommaberechnungen, sondern ist im Gegensatz hier mit 3 Fliesskommarecheneinheiten sogar besser als ein Pentium 3. Was sieht man davon in den Benchmarks? Nun um es vorwegzunehmen: 3 Fliesskommarecheneinheiten und kürzeren Takt beschleunigten die Berechnungen auf Spitzenwerte von 670 MFlops, also 10 mal schneller als beim nur 3,5 mal langsameren AMD K6-2 mit 350 MHz. Theoretisch (bei reinen Register/Register Operationen sollten 1200-1800 MFlops drin sein, man nähert sich also besser dem was man erwarten könnte, wenn man von Hand in Assembler kodieren würde.

Doch dann kommt der Autor auf recht dumme Ideen: Wie sieht es z.B. aus wenn man nicht eine kleine Schleife ansetzt sondern echte große Arrays die man wie zu den Zeiten der Vektorrechner in den achtzigern linear abarbeitet: z.B. so:

program Bench_Arrays;
{$APPTYPE CONSOLE} // Delphi Konsolenanwendung
uses SysUtils;
const Max = 1000000; // Sehr großes Array
var A,B,C: array[0..Max] of Double;
    Zeit : Comp;
        I,J  : Integer;
begin
  for I:=0 to Max-1 do
  begin
    A[I]:=Random(10);
    B[I]:=Random(10);
    C[I]:=Random(10);
  end;
  Zeit:=TimeStampToMSecs( DateTimeToTimeStamp(Now));
  for J:=1 to 100 do
  for I:=0 to Max-1 do
  begin
    B[I]:=B[I]*A[I];
    C[I]:=C[I]*A[I];
  end;
  Zeit:=TimeStampToMSecs( DateTimeToTimeStamp(Now))-Zeit;
  WriteLn(Zeit:6:0);
  ReadLn;
end.

Dieser Benchmark addiert und multipliziert ein 1 Million Elemente umfassendes Array 100 mal. Eine Cray 1 die optimiert für solche Aufgaben ist hätte das 1976 in ca. 1.5 Sekunden geschafft. Wie schnell ist nun mein Rechner der in den anderen Benchmarks etwa 4 mal schneller als die Cray 1 war? Sie ahnen es schon: er bricht rapide ein. In dem hier gezeigten Beispiel unter Delphi 5 von 670 MFlops auf 29. In C noch mehr, da das angeblich schnelle Durchlaufen eines Arrays über Pointer mit einer doppelt indirekten Adressierung erreicht wird [ebx+edx] anstatt [ebx] direkt zu nehmen wie beim Delphi Programm).

Daran erkennt man recht deutlich die Grenzen: Ein Prozessor kann noch so schnell ein, wenn er auf Daten warten muss. Dank nur 8 Fliesskommaregister in "Stapelbetriebsweise" ist ja ein x86 Prozessor schon langsamer als ein Alpha oder Itanium - Systeme mit 32 bzw. 128 Fliesskommaregister. Doch bei Daten übers RAM wird es schleichend: Mein PC-133 Speicher ist eben 9 mal langsamer getaktet als der Prozessor und hat noch dazu Zykluszeiten von 7-8 ns anstatt 2 ns wie die Cache RAM's. Da wundert es einen nicht, das dieser Benchmark 7 Sekunden dauert - es werden in dieser Zeit 4.8 GByte über den Bus geholt, dabei kann der Compiler nur den Zugriff auf Array "A", das zweimal lesend verwendet wird gut optimieren. Die resultierende Datenrate von 685 MByte/sec liegt nur knapp unter dem Wert den der Speicher liefert (8 Bytes × 133 MHz = 1064 MByte/sec ohne Berücksichtigung von Wartezyklen und Rechenzeit im Prozessor).

Aus dieser Hinsicht ist es von Vorteil mehr Register im Prozessor zu haben und schnelles RAM über einen breiten Bus ansprechen zu können. Ersteres ist bei fast allen modernen CPU's gegeben, die zwischen 32 und 256 Register haben - nicht nur 8 wie bei der x86 Architektur. Zweitens kann in Supercomputern entscheidend über die Performance sein. Schnelleres RAM gibt es, nur ist es ziemlich teuer. Schon beim PC gibt es RAMBUS oder das RAM auf den Grafikkarten als Alternative - beide mit etwa der doppelten Performance von DDR-RAM.

Resümee

Natürlich spielt der Compiler eine Rolle. FPK zeigte mit Spitzenwerten von 60 MFlops beim K6-2 eine doppelt so gute Performance wie Delphi 4. Und dies obgleich ich die MMX Erweiterungen für 3D-NOW nicht zum laufen bekam, weil der integrierte und externe Assembler die Opcodes nicht kannten. Sonst hätte man für Single Precision noch bessere Werte erreichen können. Aber auch mit trickreichen Kombinationen kann man viel erreichen.

Lehren für die Praxis:

Was mich allerdings erstaunt hat, wie wenig die Compiler die FPU Register ausnutzen. Es handelt sich ja nun nicht gerade um mordsmäßig komplizierte Benchmarks sondern um 6 Programmzeilen mit 6 Variablen die ohne Problem in die FPU Register gepasst hätten und bei 100 Millionen Durchlaufen eine Menge Zeit gespart hätten. Doch nur ein Compiler hat ein einziges FPU Register als Zwischenspeicher genutzt. Lediglich FPK unterstützt eine Option Variablen in Register zu packen. Immerhin erreicht er bei einer FPU Operation eine Spitzenleistung von 79 MFlops bei Single Werten (100 Millionen/1.26). Allerdings muss man im "realen" Leben noch die Schleife dazu zählen die 1.21 sec lang ist. Praktisch nutzbar sind also 40 MFlops. (Werte für den AMD-K62 350 MHz).

Wenn nun C-Liebhaber lächeln und sich selbstgefällig zurücklehnend was von Register Variablen murmeln, so sei Ihnen gesagt das sowohl der GCC wie auch der BCC 5.5 bei expliziter Deklaration als Register keine FPU Register nutzen sondern alle über ptr[reg+offset] zugreifen und damit in etwa die Werte von Delphi 4 erreichen, aber nicht die von FPK oder Vpascal. Kurzum: Compiler Hersteller es liegt an euch Abhilfe zu schaffen!

Allerdings haben die Optimierungen auch ihren Preis, denn die Größe der .EXE stieg mit der Geschwindigkeit an. Sie liegt bei Turbo Pascal 5.5 bei 19 K, bei 7.0 bei 25 K, bei Vpascal bei 29 K. Die beiden Delphis brauchen schon je 50 K und FPK liefert eine 140 K große EXE ab, also 7 mal so groß wie die von Turbo 5.5.

Zuletzt nun doch noch nachgereicht die Erklärung warum Hersteller von Prozessoren erheblich bessere Werte angeben. Alle Compiler - Egal ob C oder Pascal, ob Freie Software oder Microsoft oder Borland - machten bei Benchmarks nur von der "alten" FPU gebrauch, und zwar unabhängig davon was man als Codeplattform angegeben hat. Seit dem Pentium 3 gibt es aber SSE. Damit können in FPU Registern (die nun intern 128 Bit breit sind) jeweils 4 Werte einfacher Präzision auf einmal bearbeitet werden, seit dem Pentium 4 auch 2 Double Werte. Damit ist es natürlich möglich 4 Werte mit einer Instruktion zu berechnen. Es fallen auch Lade und Speicherzyklen in geringerem Maße an (nach meinen Tests machen diese inzwischen erheblich mehr Zeit aus als das eigentlich Rechnen). Dies allerdings nur sofern man sich mit einfacher Präzision zufrieden gibt und es die Aufgabenstellung zulässt.

Doch gerade bei dem populären Matrizen Benchmarks ist genau dies der Fall, und dann kommen Werte heraus, die 3-4 mal höher sind als die welche ein Compiler für Otto Normalverbraucher von Microsoft, Borland oder IBM erzeugt, die aus Komptabilität ihre Finger von allen neuen Opcodes seit dem 486 lassen, also auch von MMX, 3D-NOW und SSE. Das Angeben der Codeplattform beeinflusst meinen Erfahrungen nach nur das Gruppieren von Befehlen für parallele Ausführung oder bei 2 Befehlen die dasselbe bewirken sich innerhalb der verschiedenen Prozessoren in der Ausführungszeit unterscheiden welcher genommen wird.

Neue Architekturen - Was bleibt?

Pentium 4 Prozessor dieWenn man das Problem von der Kundenseite aus sieht, so stellt es sich natürlich anders dar. Als Programmierer ist man immer bestrebt das maximale aus seiner Maschine herauszuholen. Ein Kunde will ein Programm dass er auf jedem Rechner installieren kann. Wer schon mal etwas tiefer mit Windows Programmen zu tun hatte, merkt sehr bald, dass dies nicht so leicht ist. Sobald man etwas schickere Windowselemente benutzt oder API Funktionen merkt man dass es hier schon feine Unterschiede gibt. Noch mehr gibt es aber in der Prozessorplattform. Ein Windows 9x Programm muss praktisch fähig sein auf allen Prozessoren vom 486 aufwärts zu laufen.

Daher spielen heute Optimierungen für eine Codeplattform bei den Herstellern keine Rolle mehr. Wenn man z.B. Code für dein Pentium 4 optimiert, der nur eine FPU hat, so läuft dieser auf Athlons, aber auch Pentium II+III mit 2 bzw. 3 Fliesskommaeinheiten langsamer. Das gleiche gilt für trickreiche Optimierungen der Befehle, abgestimmt auf die Pipeline Länge des Prozessors.

Techniken wie MMX, 3DNOW, SSE und SSE2 sind zwar verfügbar. Doch jeder der kommerzielle Programme betreibt wird diese selten nutzen, denn es bedeutet dass man bestimmte Kunden ausschließt So wundert es nicht, dass Microsofts C++ Compiler 7.0 im Jahre 2003 die Unterstützung für Pentium II und III Prozessoren einführt, also Prozessoren die zu diesem Zeitpunkt schon 4-6 Jahre alt sind. (Aber noch keine für die neueren Athlons oder Pentium 4). Wer wirklich die optimale Performance haben will muss dann zu den Compilern von Intel und AMD greifen. Das mag Sinn machen wenn die Software für einen Kunden programmiert wird und man die Hardware kennt, aber nicht für den allgemeinen Markt. Im Extremfall ist die Software auf älteren Prozessoren nicht mehr ausführbar. Selbst wenn man sich nur auf Optimierungen beschränkt, d.h. die Befehle so zusammenbaut, dass ein Prozessor diese ohne Verzögerungen ausführen kann, so kann es auf einem anderen Prozessor zu einem Leistungseinbruch kommen. Als Beispiel sei hier der Windows Media Player 7.0 genannt, der im Code die Instruktionen so anordnete, dass sie wenn vorhanden zwei FPU's nutzten, die jeder Prozessor seit dem Pentium II hatte. Bei einem Pentium 4 mit einer FPU war der Media Player so 50 % langsamer als auf einem niedrig getakteten Pentium III.

Wir sehen auch hier die Crux die eine Architekturweiterentwicklung mit sich bringt. Früher waren die Schritte groß - Von 8086 -> 80286 war es der Sprung von DOS auf Protected Mode fähige Betriebsysteme wie OS/2 und Windows 3.1. Beim Sprung 80286->80386 war es der auf ein echtes 32 Bit System wie Windows NT oder Linux. Heute sind es die Fähigkeiten bestimmte Aufgaben wie Videodekodierung schneller zu lösen, aber es sind keine epochalen Sprünge. Selbst der Schritt von 32 auf 64 Bit ist nur halbherzig und wird von den Softwareherstellern weitgehend ignoriert. Was bringt es dem Kunden ? Die früheren Schritte brachten ihm eine grafische Oberfläche (Windows 3.1 auf einem 80286) und ein sicheres System (Windows NT auf einem 80386). Heute wird es nur etwas schneller, aber der echte Zusatznutzen bleibt aus.

Nachtrag 2005

6 Jahre später ist es an der Zeit Vergleiche zu ziehen. Ich habe dabei bewusst dieselben Benchmarks verwendet. Dies erlaubt einen Vergleich mit den alten Werten. Die alten Benchmarks liefen auf einem AMD K6-2 mit 350 MHz, die neuen auf einem AMD Athlon 64 mit 3000 MHz. Man kann natürlich nicht die absoluten Werte vergleichen, aber die Prozentwerte beim einfügen von immer mehr Werten.

Ich will mir hier das Abdrucken aller Werte sparen. Als ein Beispiel habe ich hier das Protokoll von Delphi 4 eingefügt.

Anzahl     1            2            3            4            5       6
----------------------------------------------------------------------------
Double   0.85 100%   0.88 103%   0.95 112%   0.96 113%   0.97 114%   0.91 107%  
Single   0.80 100%   0.97 121%   0.94 117%   0.96 120%   0.95 119%   1.02 127%  
Extend   2.01 100%   2.94 146%   3.35 167%   4.45 221%   5.62 279%   6.71 333%  
Longint  0.09 100%   0.09  99%   0.09  99%   0.21 232%   0.20 221%   0.23 253%  
Comp     0.33 100%   0.38 115%   1.05 318%   1.31 396%   2.48 750%   2.26 684%  
Word     0.01 100%   0.09 900%   0.18 1810%   0.82 8210%   0.89 8920%   1.00 10010%  
Mix D/L  1.62 100%   1.95 120%   0.92  57%   1.72 106%   1.90 117%   2.39 148%  



Auffällig ist folgendes:

Bei Double, Singe und dem Mix steigt die Ausführungsdauer fast gar nicht durch das Einfügen neuer Instruktionen an - Es wird also sehr viel besser parallelisert. Word braucht fast keine Ausführungsdauer (in anderen Compilern erhält man hier sogar negative Ausführungsdauern beim ersten Durchlauf, d.h. es ist schneller als eine Leerschleife.

Insgesamt ist die Paralisierung besser geworden. Es ist zu erkennen an dem langsameren Anstieg der Werte. Geht man auf neuere Versionen von Delphi und FPK über so häufen sich in den Benchmarks sogar negative Werte für die Ausführungszeit. Diese kommen dadurch zustande, dass zuerst eine Leerschleife abläuft und deren Dauer von allen Benchmarks abgezogen wird um die reinen Berechnungen zu haben. Allerdings kann dies auch ein Windows XP Effekt sein, denn anders als bei Windows 98 auf dem die alten Benchmarks liefen gibt es keine Möglichkeit einem Programm die gesamte Rechenzeit zuzuweisen.

FPK, der als Compiler sicher eine größere Weiterentwicklung durchmacht habe ich mal verglichen. Die Werte sind im wesentlichen die gleichen wie bei der alten Version. Schwankungen gibt es nur um weniger als 10 %, also vernächlässigbar. Irgendwelche eingeschalteten Code Optimierungen oder ein bestimmter Prozessor als Ziel haben auf die Werte ähnlich geringen Einfluss.

ieser Text stammt von Bernd Leitenberger
© des Textes: Bernd Leitenberger. Jede Veröffentlichung dieses Textes im Ganzen oder in Auszügen darf nur mit Zustimmung des Urhebers erfolgen.

Zum Thema Computer ist auch von mir ein Buch erschienen. "Computergeschichte(n)" beinhaltet, das was der Titel aussagt: einzelne Episoden aus der Frühzeit des PC. Es sind Episoden aus den Lebensläufen von Ed Roberts, Bill Gates, Steve Jobs, Stephen Wozniak, Gary Kildall, Adam Osborne, Jack Tramiel und Chuck Peddle und wie sie den PC schufen.

Das Buch wird abgerundet durch eine kurze Erklärung der Computertechnik vor dem PC, sowie einer Zusammenfassung was danach geschah, als die Claims abgesteckt waren. Ich habe versucht ein Buch zu schreiben, dass sie dahingehend von anderen Büchern abhebt, dass es nicht nur Geschichte erzählt sondern auch erklärt warum bestimmte Produkte erfolgreich waren, also auf die Technik eingeht.

Die 2014 erschienene zweite Auflage wurde aktualisiert und leicht erweitert. Die umfangreichste Änderung ist ein 60 Seiten starkes Kapitel über Seymour Cray und die von ihm entworfenen Supercomputer. Bedingt durch Preissenkungen bei Neuauflagen ist es mit 19,90 Euro trotz gestiegenem Umfang um 5 Euro billiger als die erste Auflage. Es ist auch als e-Book für 10,99 Euro erschienen.

Mehr über das Buch auf dieser eigenen Seite.

Hier geht's zur Gesamtübersicht meiner Bücher mit direkten Links zum BOD-Buchshop. Die Bücher sind aber auch direkt im Buchhandel bestellbar (da ich über sehr spezielle Themen schreibe, wird man sie wohl kaum in der Auslage finden) und sie sind natürlich in den gängigen Online-Plattformen wie Amazon, Libri, Buecher.de erhältlich.

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