Automatische Optimierung durch den Compiler

Ohne Optimierungsoption (-On, n=0,1,2,3) bzw. mit der Option -no führt der Compiler nur Grundoptimierungen durch, die den Code an die Hardware der Maschine anpassen. Im folgenden sollen die verschiedenen Optimierungsstufen kurz vorgestellt werden. Dabei beinhalten die höheren Stufen immer alle Operationen der niedrigeren Stufen.
In der Stufe -O0 werden maschinenunabhängige Optimierungen innerhalb eines Blocks durchgeführt, d.h. innerhalb einer Folge von Befehlen ohne irgendwelche Sprünge (DO, IF, ...). Beispiele sind das Eliminieren von mehrfachen Zuweisungen an eine Variable, die zwischendurch nicht benutzt wird oder das Einführen von temporären Variablen, um Zwischenschritte abzuspeichern, die mehrmals benutzt werden.
In der Stufe -O1 kommen skalare Optimierungen auf der Ebene von Prozeduren dazu, z.B. wird eine Variable, der eine Konstante zugewiesen wurde, durch diese Konstante ersetzt oder skalare Zuweisungen in Schleifen herausgezogen.
Erst mit Optimierungsstufe -O2 werden Vektorbefehle verwendet. Der Compiler analysiert verschiedene Formen von Schleifen und wandelt sie, soweit möglich, in Vektorkommandos um. Er führt zum Teil eine ganze Reihe recht komplizierter Umformungen durch, um diese ''Vektorisierung'' zu ermöglichen oder effektiver zu machen. Allerdings gibt es einige Programm-Konstruktionen, die das automatische Erkennen und Einführen von Vektor-Befehlen verhindern. Informationen darüber, welche Schleifen, ggf. mit welchen Methoden, vektorisiert werden konnten, und welche Faktoren dies bei den anderen Schleifen verhinderten, enthält der Optimierungs-Bericht, den der Compiler ab -O2 normalerweise ausgibt. Zusätzlich kann er noch Informationen über die Vektoren selbst enthalten. Mit der Compiler-Option -or kann man festlegen, ob man nur den Schleifen-Bericht (loop), nur die Vektor-Informationen (array), beides (all) oder gar nichts (none) haben möchte. Der Default ist ''-or loop''. Wir werden uns in Abschnitt 4.3 ausführlich damit beschäftigen, wie man solche Hindernisse für die Vektorisierung u.U. beseitigen kann.
Mit der Option -O3 versucht der Compiler, den Code zu parallelisieren, d.h. in Teile zu zerlegen, die unabhängig von verschiedenen Prozessoren bearbeitet werden können. Auf einer Maschine mit vier Prozessoren, auf der fast immer sechs bis zehn Jobs gleichzeitig laufen, wird man allerdings selten mehr als eine CPU zur Verfügung haben. Die Parallelisierung von Programmen ist Thema des Abschnitts 4.4
Die konkreten Auswirkungen der verschiedenen Optimierungsstufen auf das Programm linalg werden wir durch Vergleich der Laufzeiten von linalg nach Übersetzen aller Files mit ''FFLAGS=-On'' untersuchen. Die Zeiten messen wir mit dem /bin/time-Kommando: Der Aufruf ''/bin/time linalg'' bewirkt, daß zunächst das Programm linalg normal ausgeführt wird, danach werden zusätzlich drei Zeiten ausgegeben:
  1. die ''real''-Zeit, die angibt, wieviel Zeit vom Start des Kommandos bis zum Ende vergangen ist (sie interessiert uns hier nicht, da sie wesentlich von der Auslastung der Maschine durch andere Benutzer abhängt),
  2. die ''user''-Zeit, d.h. die CPU-Zeit, die zur Ausführung des Programms gebraucht wurde, wobei Zeiten für System-Aufrufe - etwa bei Ein- und Ausgaben - nicht mitzählen,
  3. die ''system''-Zeit, die gerade die CPU-Zeit von System-Aufrufen angibt.
Die tatsächliche CPU-Zeit ist also die Summe aus ''user''- und ''system''-Zeit.
Für die verschiedenen Optimierungsstufen ergaben sich folgende Ergebnisse:

C240:  
-no 19 .0 real 9.8 user 0.1 sys
-O0 38 .2 real 8.2 user 0.0 sys
-O1 12 .8 real 4.3 user 0.0 sys
-O2 2 .3 real 1.7 user 0.0 sys
-O3 2 .5 real 2.0 user 0.1 sys
C3840:
-no 4.3 real 4.1 user 0.0 sys
-O0 3.3 real 3.2 user 0.0 sys
-O1 1.7 real 1.6 user 0.0 sys
-O2 0.7 real 0.6 user 0.0 sys
-O3 1.1 real 1.3 user 0.0 sys


Man sieht, daß schon die skalaren Optimierungen in -O1 in unserem Beispiel eine ganze Menge an Geschwindigkeitszuwachs bringen. Der größte Sprung geschieht aber beim Übergang zur Vektorisierung (-O2). Dies ist nicht weiter verwunderlich, denn die Convex ist als Vektorrechner von ihrer Hardware her speziell für Vektorbefehle ausgelegt, die aber erst ab -O2 benutzt werden. Daß beim Parallelisieren die Zeit wieder zunimmt, liegt an der Definition von ''user''- und ''system''-Zeit, die jeweils die Summen der Zeiten der einzelnen CPUs sind. Daß die real-Zeit hier niedriger ist als die user-Zeit, ist nur möglich bei Parallelisierung. Allerdings wurde diese Messung gemacht, als keine Batchjobs liefen. Normalerweise würde auch bei guter Parallelisierung die real-Zeit deutlich höher sein. Wie man auch bei normal ausgelasteter Maschine das Ausmaß an Parallel-Verarbeitung bestimmen kann, werden wir in Kapitel 4.4.3) lernen.

previous    contents     next

Peter Junglas 18.10.1993