Computer & Technik
Standard-Software
Code-Optimierung mit MS-C 6.0
Es gibt aber auch einen Wermutstropfen in Sachen Grafik. Nach wie vor unterstützt MS-C keine hochauflösenden Grafikkarten. Die höchste derzeit unterstützte Auflösung beschränkt sich auf die Standard-VGA-Auflösung von 640x480 Pixel mit 16 Farben. Anders als beim Konkurrenten Turbo-C ist unter MS-C eine Abhilfe nicht in Sicht. Denn im Gegensatz zu Borlands Graphic Interface, kurz BGI, sind bei Microsoft alle Grafikfunktionen und -auflösungen als fester Bestandteil in die Grafik-Library integriert. Ein flexibles Treiber-Handling à la BGI ist weder möglich, noch auf absehbare Zeit in Sicht. Im Zeitalter der sinkenden Hardwarepreise, wo Super-VGA-Karten mit Auflösungen von 800x600 oder 1024x768 beileibe keine Seltenheit mehr sind, ist diese veralterte Technik durchaus ein Kritikpunkt den es hervorzuheben gilt. Schließlich existieren für Turbo-C bereits BGI-Treiber, die sogar 1024x768 mit 256 Farben unterstützen.
Dafür macht der Microsoft Optimizing Compiler seinem Namen alle Ehre wenn es darum geht, superschnellen Code zu produzieren. War bereits der von Version 5.1 erzeugte Code deutlich besser als der von vergleichbaren Compilern, speziell auch Turbo-C 2.0, so ist den Microsoft-Entwicklern mit der Version 6.0 erneut ein großer Wurf gelungen.
Neben der generellen Optimierung bezüglich der Betriebssysteme DOS, OS/2 und WINDOWS kennt der Compiler eine Reihe spezieller Optimierungstechniken, von denen die meisten wahlweise an- und abgeschaltet werden können.
Nicht abschalten läßt sich die sogenannte "peep-hole"-Optimierung. Sie wird in jedem Fall angewendet. Hier betrachtet der Compiler quasi durch ein "Guckloch" (daher auch der Name) eine kleine Codesequenz und versucht diese zu optimieren. Speziell bei Schiebeoperationen läßt sich so unter Umständen sehr viel Laufzeit einsparen.
Die optionalen Optimierungen beziehen sich immer auf einen größeren Programmabschnitt und können über Pragmas für ganze Module, einige Funktionen oder einzelne Schleifen aktiviert werden. Unter der Workbench lassen sich alle Optimierungen bequem über das Options-Menü einstellen. Allerdings beziehen sie sich dann immer auf das gesamte Modul. Ist dies nicht gewünscht oder gar gefährlich, muß mit Pragmas gearbeitet werden, die dann ganz gezielt einzelne Optimierungen aktivieren oder abschalten.
Standardmäßig verwendet der Compiler immer eine 'sichere Optimierung'. Was darunter zu verstehen ist, wird im Handbuch an einem simplen Beispiel erläutert, in dem in einer Schleife eine Division durch Null auftreten kann. Die beteiligten Operanden ändern sich aber innerhalb der Schleife nicht, so daß bei einer aggressiven Optimierung die if-Abfrage aus der Schleife herausoptimiert werden könnte. Was der Compiler natürlich nicht wissen kann, ist, daß gerade durch diese Abfrage die Division durch Null, die zu einem Laufzeitfehler führen würde, abgefangen wird. Um solche Spezialfälle kontrollieren zu können, sollte eine aggressive Schleifenoptimierung in der Regel durch Setzen der Option /On (sichere Schleifenoptimierung an) vermieden werden.
Doch wie optimiert der Compiler? Ein kurzes Beispiel soll die generelle Vorgehensweise zeigen:
int i;
for (i=0;i<=1000;i++)
xyz[i]=a+b;
Der Ausdruck "a+b" ist innerhalb der Schleife konstant und bräuchte eigentlich nicht ständig berechnet zu werden. Der Compiler erzeugt daher eine temporäre Variable, die vor dem Schleifeneintritt den entsprechen Wert zugewiesen bekommt:
int i, temp;
temp=a+b;
for (i=0;i<=1000;i++)
xyz[i]=temp;
Auf diese Weise kann die Ausführunghsgeschwindigkeit drastisch erhöht werden. Gefährlich wird es erst, wenn innerhalb der Schleife durch die originären Variablenzuweisungen Seiteneffekte entstehen. Im Sinne eines guten Programmierstils und der besseren Wartbarkeit des Programmcodes willen sollte man aber auf solche Seiteneffekte grundsätzlich verzichten!
Drei weitere Optimierungstechniken, die ständig angewendet werden und allenfalls über die Option /Od (keine Optimierungen) unterdrückt werden könnten, beziehen sich wiederum auf kleinere Programmabschnitte:
- Eliminierung von Teilausdrücken
- Eliminierung von totem Speicherplatz
- Eliminierung der Konstantenausbreitung
Bei der Elimination von Teilausdrücken versucht der Compiler Ausdrücke, die mehrfach vorkommen, aber nur einmal berechnet werden müssen, einer temprären Variable zuzuweisen. Dazu ein Beispiel:
a=b+c*(d-e);
f=e*(d-e);
Der Ausdruck "(d-e)" kann hier durch eine temporäre Variable ersetzt werden, so daß sich der Code vereinfacht:
t=d-e;
a=b+c*t;
f=e*t;
Bei der Elimination von "totem Speicherplatz" geht man einen Schritt weiter. Wird eine Variable nicht mehr gebraucht, kann sie auch unterdrückt werden. Im Ausdruck
a=b+c;
d=a+e;
kann die Variable a ersatzlos gestrichen werden. Der Code vereinfacht sich dann zu
d=b+c+e;
Die dritte Standard-Optimierung betrifft die Ausbreitung von Konstanten, die sich speziell bei häufig von verschiedenen Programmierern überarbeiteten Programmteilen zu einer wahren Plage entwickeln kann, wie das folgende Beispiel zeigt:
a=5;
b=6;
c=a+b;
Dieser Code, der vielleicht durch vorangegangene Optimierungen entstanden ist, kann auf eine Konstante verkürzt werden:
c=11;