Ansätze der Optimierung per Software
Ansätze, die
Leistung der Prozessoren aufs Vollste auszureizen, gibt es einige. Deren Effektivität
und Verbreitung steht jedoch auf einem ganz anderen Blatt. Auch gibt es Techniken,
die zwar eine hervorragende Effektivität aufweisen können, jedoch
leider Änderungen an der Hardware voraussetzen. Somit stellen diese Lösungen
technisch betrachtet die optimale Lösung für neue Hardware dar. Markttechnisch
betrachtet ist diese Lösung jedoch alles andere als optimal, schließlich
folgt im Rattenschwanz der Konsequenzen die Inkompatibilität zum bestehenden
x86 Standard.
Die im folgenden vorgestellten Techniken sind geordnet, vom untersten Level,
der Optimierung einzelner Instruktionen und Befehle, über Steigerung des
Parallelismus von Instruktionen und Threads bis hin zu Änderung des Befehlssatzes
um einen möglichst hohen TLP mit hohem ILP (siehe
nächste Seite) bei
optimaler Anpassung an die Hardware zu erreichen. Aber der Reihe nach.
Optimierung
der Compiler auf bestimmte Befehlssätze / Generationen
Auf der untersten Ebene steht die Optimierung einzelner Instruktionen an bestehende
Hardware/Befehlssätze. Früher, als Assembler noch Standardsprache
für jeden Programmierer war, stellte Programmieren noch echte "Fieselarbeit"
dar. Register wollten einzeln belegt, addiert oder gelöscht werden, Speicher
separat zugewiesen, etc. Der Spruch "Was man nicht mit Assembler programmieren
kann, muss man halt löten" besitzt erstaunlich hohen Realitätsbezug,
jeder Assembler-Programmierer wird sich lächelnd erinnern.
Mit dem Erfolg der sog. hohen Programmiersprachen, änderte sich dieser
Zustand jedoch schlagartig. Plötzlich war der Programmierer nicht mehr
Herr über den im Endeffekt von ihm produzierten Maschinencode, sondern
musste diese Kontrolle dem Compiler übertragen (to compile = übersetzen,
kompilieren; Compiler übersetzen die Befehle höherer Programmiersprachen
in für den Prozessor verständlichen Maschinencode). Die Programmierer
selber haben hierbei keinerlei Einfluß auf die tatsächliche Laufzeit
der eigenen Programme und müssen sich auf den Compiler verlassen.
Da der Mensch jedoch nicht perfekt ist und Prozessoren sich beständig weiterentwickeln,
erzeugen Compiler selten perfekten Maschinencode und altern zudem relativ schnell.
Dennoch darf man die Rolle der Compiler auf keinen Fall unterschätzen,
denn hier liegt das größte, per Software erreichbare Optimierungspotenzial
welches die zwingend erforderliche x86-Kompatibilität wahrt.
Compiler arbeiten
auf eine bestimmte Art und Weise: Jeder noch so komplexe Befehl, jede noch so
große, lange oder verschachtelte Schleife einer hohen Programmiersprache,
kann in einzelne, sog. Elementarbefehle zerlegt werden und somit letztendlich
in Maschinenbefehle. Unterschiedliche Prozessorgenerationen verarbeiten jedoch
ein und denselben Maschinenbefehl unter Umständen auf komplett unterschiedliche
Art und Weise mit signifikant unterschiedlicher Laufzeit. Somit muss ein guter
Compiler möglichst viele verschiedene Arten der Optimierung beherrschen,
je nach Generation des zu verwendenden Ziel-Prozessors. So benötigt die
i686 Architektur beispielsweise eine komplett andere Reihenfolge und Anordnung
der Befehle an den Decoder als die i786 Architektur und noch anders gar die
K7 Architektur.
Letztendlich liegt hier das größte Potenzial, aufgrund der Inkonsistenz
der Prozessoren und ihres Befehlssatzes jedoch auch das größte Konfliktpotenzial.
Schließlich läuft Software, die speziell für neuere Prozessoren
optimiert wurde (Beispielsweise durch Nutzung der SSE/SSE2 Einheiten oder zusätzlicher
CISC/RISC Befehle) nicht immer auch auf älteren Prozessoren. Im Markt der
ewig kompatiblen x86-Prozessoren ist so etwas für viele Firmen nicht vertretbar,
weshalb sich proprietäre Befehlserweiterungen auf lange Sicht nicht durchsetzen
konnten. Drei bekannte Beispiele sind MMX, 3DNow! und SSE.
Diesen Artikel bookmarken oder senden an ...