Doping für CPUs — Möglichkeiten der Leistungssteigerung
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.