Leistungsmessung für RISC- und CISC-ProzessorenTeil 2

Für die Leistungsfähigkeit von Systemen zählen nicht nur MIPS

08.02.1991

Zwei Argumente werden bei der Diskussion der Vorteile von RMC-Architekturen immer wieder ins Feld geführt: hohe Rechenleistung und einfacher Entwurf. Mit dem Aufkommen hochleistungsfähiger RISC-Workstations entfachten auch wieder die Diskussion um Leistungsmessungen (Benchmarks) von Rechnerarchitekturen. Im zweiten Teil der CW-Serie werden die Benchmarks-Tests im einzelnen abgehandelt.

In einem ausführlicheren Artikel (11) wurden die Eigenschaften dieser Benchmarks vergleichend dargestellt. Dort findet man auch nähere Angaben zur Entstehungsgeschichte dieser Benchmarks. Die folgende Darstellung faßt die wesentlichen Aussagen zusammen. Die Prozentzahlen in den Tabellen beruhen jeweils auf dynamischen Messungen der Programme, spiegeln also das Verhalten im Ablauf wider. Alle Messungen wurden auf einer VAX 11/785 mit Compilern von BSD UNIX 4.3 vorgenommen.

In der Tabelle, die die Verteilung der Operanden-Datentypen darstellt, spiegelt sich wider, daß Whetstone und Linpack repräsentativ sein sollen für numerische Berechnungen, in denen Floating-Point-Daten dominieren. Dhrystone dagegen soll repräsentativ sein für Aufgaben der Systemprogrammierung und erhält deshalb absichtlich keine Floating-Point-Operationen.

Bei näherer Betrachtung findet man aber wesentliche Unterschiede zwischen den beiden Floating-Point-Benchmarks Whetstone und Linpack. Sie werden am deutlichsten, wenn man die Verweilzeit der Programme in den verschiedenen Unterprogrammen (Prozedur-Profil) betrachtet.

Whetstone verbringt den überwiegenden Teil der Laufzeit gar nicht im compilierten Code, sondern in den mathematischen Funktionen des Laufzeitsystems. Dies entsprach der beobachteten Verteilung in den Programmen im "National Physical Laboratory" (England), die der Konstruktion von Whetstone zugrundelagen, es muß nicht unbedingt typisch sein für andere Numerik-Programme.

Linpack wiederum - kein explizit für Benchmark-Zwecke konstruiertes Programm, sondern ein Anwendungs-Programmpaket aus der linearen Algebra - verbringt den allergrößten Teil seiner Zeit in einem 20-Zeilen-Unterprogramm, und ist im wesentlichen mit der Addition und Multiplikation von Vektorelementen in einer Schleife beschäftigt. Dies ist typisch für einen größeren Bereich von Numerik-Programmen, muß aber für andere, auch Numerik-Anwendungen, nicht repräsentativ sein.

Bei Dhrystone sind, vor allem in der meist benutzten C-Version, die beiden - vorkommenden String-Operationen überrepräsentiert: Strings treten zwar in der Textverarbeitung häufig auf, sie sind aber hier mit 30 Zeichen länger als im Durchschnitt. Deshalb sollte man, wenn Dhrystone-Ergebnisse zum Vergleich von Prozessoren herangezogen werden, sicherstellen, daß die verwendeten Sprachsysteme (Compiler, Laufzeitsystem) die Stringfunktionen vergleichbar behandeln; sonst können unterschiedlich weitgehende Compiler-Optimierungen die unterschiedlichen Prozessor-Geschwindigkeiten überschatten.

Typisch für den Unterschied zwischen Systemprogrammierung und numerischer Programmierung ist, daß "for"-Schleifen in Numerik-Programmen wesentlich häufiger sind, während Systemprogramme mehr "if"-und "case"-Anweisungen sowie Prozeduraufrufe enthalten. Auch sind bei Zuweisungen die Ausdrücke auf der rechten Seite bei Systemprogrammen meist sehr viel einfacher, Zuweisungen wie i:=i+1 sind hier gar nicht selten. Insofern spiegeln die Benchmarks durchaus die jeweiligen Anwendungsgebiete wieder. Eher zufällig dagegen ist bei Whetstone und Linpack die Verteilung der Lokalität der Operanden.

Eine der wesentlichen Eigenschaften von RISC-Prozessoren ist, daß lokale Variable und Parameter, wenn irgend möglich, in Registern allokiert werden (RISC-Prozessoren haben im allgemeinen wesentlich mehr Register als CISC-Prozessoren). Davon profitiert Dhrystone sehr stark, Whetstone dagegen überhaupt nicht.

Ein hoher Anteil von lokalen Variablen und Parametern entspricht allerdings auch einem vom Software-Engineering geforderten Programmierstil (bessere Modularisierung, Koppelung durch Unterprogramme statt durch globale Daten), so daß diese "Bevorzugung von RISC-Strukturen durch Dhrystone durchaus gerechtfertigt erscheint.

Wesentlich für die Einschätzung von Benchmark-Tests ist noch, ihre Größe Oberhaupt zu kennen. Man sieht, daß Whetstone-, Linpack-, Dhrystone sowie andere Programme so klein sind, daß der Code aus dem Cache heraus ausgeführt werden kann, oft paßt er sogar in den On-Chip-Befehlscache neuerer Mikroprozessoren.

Dies spiegelt natürlich nicht das Verhalten echter Programme wieder, bei denen der Befehlsstrom im Adreßraum hin und her wandert (bei Systemprogrammen wesentlich mehr als bei Anwendungsprogrammen) - die kleinen Benchmarks zeichnen also ein zu optimistisches Bild. Beim Datenbereich ist nur Linpack etwas größer, hier muß man darauf achten, daß Ergebnisse nur für Programmversionen mit gleicher Datengröße miteinander verglichen werden. (Bei der Standardversion sind es 100 x 100 Floating-Point-Zahlen von einfacher oder doppelter Genauigkeit, es gibt aber auch Versionen mit größeren Matrizen.)

Hersteller von - Computern und besonders von Mikroprozessoren werden immer versuchen, ihre Kunden mit günstigen Benchmark-Zahlen zu beeindrucken. Allzuoft entsteht dabei der Eindruck, die gemessene Leistung liege ausschließlich an der Geschwindigkeit des Prozessors. Sicher ist für den Endkunden letztlich die Kombination aus allen Einflüssen maßgeblich. Zu einer genaueren Analyse sollte man aber doch wissen, was außer der CPU noch zur gemessenen Geschwindigkeit beiträgt. Deshalb sollen in den folgenden Abschnitten noch einmal die wesentlichen Einflußgrößen erörtert werden.

Pascal und Ada schneller als C

Auch wenn dieselben Programme, in verschiedene Programmiersprachen übersetzt, ähnlich aussehen und auch die gleichen Ergebnisse liefern, gibt es sprachlich bedingte Unterschiede, die sich in der Laufzeit auswirken können. Bei Dhrystone zum Beispiel ist, gleich gute Compiler vorausgesetzt, die Pascal- und die Ada-Version üblicherweise schneller als die C-Version. Das liegt daran, daß zwei Anweisungen vorkommen, in denen Strings im Ganzen manipuliert werden, eine String-Zuweisung und ein String-Vergleich.

In Pascal und Ada ist die Länge der Strings Teil der Typin-Formation, die zur Übersetzungszeit bekannt ist, der Compiler kann daher Inline-Code erzeugen. In C dagegen gibt es keine Operationen für String-Zuweisung und Vergleiche; es werden Unterprogramme aufgerufen, die außerdem wegen der Strung-Semantik in C jedes einzelne Byte abprüfen müssen, ob es schon das Null-Byte ist, das das Ende des Strings angibt.

Ein Compiler, der durch Ausschöpfung aller Optimierungsmöglichkeiten schnellen Code erzeugt, ist bei jeder Rechnerarchitektur von Vorteil. Bei RISC-Architekturen erwartet man aber vor allem besonders gut optimierende Compiler. Beispiel hierfür ist das Vermeiden unnötiger Load- und Store-Befehle durch Common Subexpression Elimination" oder maschinenspezifische Optimierungen wie Befehls-Umsortieren zum optimalen Ausnutzen der Pipeline.

Tabelle 8 zeigt am Beispiel des C-Compilers von Mips für MIPS M/2000 (7), wie stark die Laufzeit eines Benchmark-Programms von Optimierungen des Compilers beeinflußt werden kann. Wenn man an einem Vergleich nur von Prozessoren interessiert ist, sollte man sicherstellen, daß Benchmark-Ergebnisse, die man vergleicht, mit Compilern etwa gleicher Optimierungsstufe erzielt wurden. Bei sehr stark optimierenden Compilern besteht außerdem die Gefahr, daß in Ausnahmefällen trotz aller gegenseitigen Absichten die Optimierung doch die Semantik des Codes ändert. Eine solche Optimierung, die nur für eindrucksvolle Benchmark-Zahlen und nicht für Produktionsprogramme gut ist, sollte man natürlich gar nicht erst verwenden.

Wenn ein Benchmark-Programm zu populär wird, besteht auch die Gefahr, daß Compiler-Autoren versuchen, die Optimierungen in ihren Compilern so auszuwählen, daß gerade das betreffende Benchmark-Programm schnell ausgeführt wird, selbst wenn solche Optimierungen sich anderswo wenig oder gar negativ auswirken. Von solchen Compiler-Verrenkungen" hat man für Whetstone- und Dhrystone-Tests gehört; im PC-Bereich spielt das Programm "Sieve of Eratosthenes eine ähnliche Rolle.

Nur bei näherem Hinsehen sieht man, daß außer der CPU und dem Compiler bei Laufzeitmessungen noch eine dritte Komponente beteiligt sein kann, das Laufzeitsystem der jeweiligen Programmiersprache. So wird beim Whetstone-Benchmark 40 bis 50 Prozent der Laufzeit in den Routinen der mathematischen Unterprogrammbibliothek (beispielsweise sqrt, sin, cos ...) verbracht.

Wenn diese Programme optimal codiert werden, kann das Benchmark-Ergebnis wesentlich verbessert werden. Selbstverständlich muß man wissen, ob auch schnelle Benchmark-Versionen der mathematischen -Funktionen noch die Genauigkeitsanforderungen der einschlägigen Normen (zum Beispiel IEEE Floating Point Standard) erfüllen.

Bei Dhrystone gilt ähnliches, wenn auch meist nicht ganz so kraß, für die String-Funktionen. Bei sehr schnellen Maschinen und bei hochoptimierenden Compilern kann für die C-Version der Anteil der String-Funktionen an der Laufzeit höher als in Tabelle 4 angegeben werden. Bei fast allen der kleineren Benchmark-Programme, die üblicherweise verwendet werden, muß man sich darüber im klaren sein, daß sie in bezug auf die Speicherzugriffe nicht realistisch sein können: Bei echten Programmen aus der Praxis gibt es natürlich Programmschleifen, in denen sich das Programm eine Zeitlang aufhält.

Hit-Rate immer 100 Prozent

Die Ausführung wandert im Lauf der Zeit aber doch stärker im Adreßraum herum, sowohl für den Code als auch für die Daten. Die bisherigen Benchmark-Programme dagegen sind aus Gründen der Einfachheit meist recht klein. Damit überhaupt eine Ausführungszeit meßbar ist, werden sie üblicherweise in eine "Meß-Schleife" eingeschlossen, die dann entsprechend oft durchlaufen wird. Daher beträgt schon bei recht kleinen Caches die Hit-Rate fast immer 100 Prozent.

Dementsprechend können kleine synthetische Benchmark-Programme nicht repräsentativ sein bezüglich der zu erwartenden Mischung aus Cache-Hits und Cache-Misses. Besonders auffällig ist der Performance-Spring bei Caches, die bei den neuesten Mikroprozessoren auf dem Prozessor-Chip selbst integriert sind (zum Beispiel 256 Byte Befehlscache für Motorolas 68020- und 68030-CPU, 512 Byte für den Chip 32532 von National Semiconductor sowie vier KByte für Intels 860-RISC-Prozessor).

Hier kann ein Benchmark-Programm der passenden Größe ganz erheblich beschleunigt werden, wenn es plötzlich vollständig in einen solchen Cache paßt, und diese Beschleunigung ist sicher nicht repräsentativ für allgemeine Programme.

Bei den Benchmarks, bei denen die Datengröße durch einen Parameter eingestellt werden kann, muß man darauf achten, welche Datengröße einer vom Hersteller angegebenen Leistungszahl (zum Beispiel Linpack-MFLOPS) zugrunde liegt Schließlich können bei Benchmarks, die stark speicherabhängig sind, selbst so triviale Dinge wie die Änderung von Array-Obergrenzen von 200 auf 201 zu einem drastisch veränderten Cache-Verhalten und damit zu einer ganz anderen MFLOPS-Zähl führen. (wird fortgesetzt)