Früher habe ich viel Assembler geschrieben. Es ist nicht nur so, dass die Compiler besser geworden sind, es ist auch so, dass die meiste Hardware jetzt eine Menge Logik hat, die der Ausführung von Code außerhalb der Reihenfolge gewidmet ist. Das eigentliche Kleinstproblem ist die Zeitplanung. Die meisten Computeranweisungen benötigen mehrere Maschinentakte, um ein Ergebnis zu erzielen. Eine Speicherbelastung, die den Cache verfehlt, kann mehrere Hundert dauern. Die Idee war also, andere Anweisungen einzuplanen, um etwas Nützliches zu tun, anstatt auf ein Ergebnis zu warten. Und moderne Maschinen können mehrere Befehle pro Taktperiode ausgeben. Als wir anfingen, HW außer Betrieb zu setzen, stellte ich fest, dass der Versuch, mit Handcodierung eine großartige Leistung zu erzielen, zu einem Becherspiel wurde. Erstens würde die außer Betrieb befindliche HW die Anweisungen nicht in Ihrer sorgfältig ausgearbeiteten Reihenfolge ausführen. Die ausgefallene neue HW-Architektur hat den Nachteil einer unzureichenden Software-Planung so weit verringert, dass der Compiler in der Regel nur wenige Prozent Ihrer Leistung erbrachte. Außerdem fand ich heraus, dass Compiler mittlerweile bekannte, aber komplexitätserzeugende Tricks implementieren, wie z. B. Abrollen, Laden von unten, Software-Pipelining usw. Unter dem Strich muss man wirklich hart arbeiten, einige dieser Tricks überspringen und der Compiler schlägt dich. Verwenden Sie sie alle und die Anzahl der benötigten Assembler-Anweisungen erhöht sich um ein Vielfaches!
Noch wichtiger ist wahrscheinlich, dass es bei den meisten Leistungsproblemen nicht um die Anzahl der Befehlsausgaben geht, sondern darum, die Daten in die CPU zu übertragen. Wie oben erwähnt, beträgt die Speicherlatenz jetzt Hunderte von Zyklen, und die CPU kann mehrere Befehle pro Taktperiode ausführen. Sofern das Programm und insbesondere die Datenstrukturen nicht so ausgelegt sind, dass die Cachetrefferrate bei dem Befehl übermäßig hoch ist, erfolgt eine Mikroabstimmung Level wird keine Auszahlung haben. Genau wie militärische Typen sagen, Amateure reden über Taktik, Profis reden über Logistik. Leistungsprogrammierung ist jetzt mehr als 90% Logistik (bewegte Daten). Und dies ist schwer zu quantifizieren, da die moderne Speicherverwaltung in der Regel mehrere Cache-Ebenen aufweist und virtuelle Speicherseiten von einer als TLB bezeichneten Hardwareeinheit verarbeitet werden. Auch die Ausrichtung von Adressen auf niedriger Ebene wird wichtig, da die tatsächlichen Datenübertragungen nicht in Byte-Einheiten erfolgen. oder sogar 64-Bit-Long-Longs, aber sie kommen in Einheiten von Cache-Zeilen. Dann verfügen die meisten modernen Maschinen über Hardware, die versucht, vorherzusagen, welche Cache-Zeilen in naher Zukunft möglicherweise nicht benötigt werden, und automatische Prefetches ausgibt, um sie in den Cache zu bekommen. Die Realität ist also, dass die Leistungsmodelle bei modernen CPUs so komplex sind, dass sie fast unverständlich sind. Sogar detaillierte Hardware-Simulatoren können niemals mit der exakten Logik der Chips mithalten, so dass eine exakte Abstimmung einfach nicht mehr möglich ist.
Es ist immer noch Platz für eine Handcodierung. Mathematische Bibliotheken (wie die Exp-Funktion) sowie die wichtigeren linearen Algebra-Operationen (wie Matrixmultiplikation) werden in der Regel noch von Experten handcodiert, die für den Hardwareanbieter (z. B. Intel, AMD oder IBM) arbeiten, aber wahrscheinlich nur Ich brauche ein paar erstklassige Assembler-Programmierer pro Megacomputer.