Was heißt inline in C++?
Wenn in C++ eine Funktion oder eine Methode einer Klasse als inline deklariert wird, ist dies ein Hinweis an den Compiler, den Code der Funktion direkt in die aufrufende Funktion hineinzukompilieren. Das Kompilat enthält also keinen Funktionsaufruf und Rücksprung zu einer anderen Funktion. Für oft aufgerufene, kleine Funktionen kann dies einen deutlichen Unterschied in der Laufzeit ausmachen. Außerdem sind durch Analyse der Parameter einer als inline deklarierten Funktion möglicherweise Vereinfachungen in der Programmlogik möglich, so dass der Code der inline-Funktion für einen Funktionsaufruf unter Umständen nicht vollständig verwendet werden muss.
Behandlung des inline-Hinweises im GCC
Ende Juli 2003 wurde in der Mailing-Liste des GCC erörtert, dass der GCC
den inline-Hinweis für die Standard-Potenzfunktion std::pow(T, int)
selbst für triviale Potenzen (z.B. 1 oder 2)
nicht befolgt. Das führte zu erhitzten Diskussionen über die Behandlung von inline-Deklarationen im GCC allgemein.
Dabei gab es zwei Lager: Die eine Gruppe vertrat die Meinung, dass der Compiler besser als der Anwendungsentwickler
in der Lage sein sollte, zu entscheiden, ob eine Funktion geinlined werden sollte oder nicht. Diese Gruppe war also
der Ansicht, dass der GCC inline-Deklarationen weitgehend ignorieren sollte (was das momentane Verhalten
des GCCs ist). Die andere Gruppe vertrat die Ansicht,
dass der GCC inline-Deklaration wann immer möglich befolgen
sollte, selbst wenn es zum Schaden für
die Geschwindigkeit eines Programms wäre. Wenn zu große Funktionen geinlined werden, kann das Program viel
größer werden; dadurch passt es unter Umständen nicht mehr in den Cache des Prozessors und wird dann
langsamer ausgeführt. So etwas sollte aber in der Verantwortung des Programmiers liegen.
Test des Inlining-Verhaltens von GCC für VRS
Mit der GCC-Option --inline-limits=10000000
kann man die eingebaute Heuristik des GCC
für das Inlining umgehen. Alle als inline deklarierten Funktionen werden dann, falls möglich, geinlined.
Ich habe dies mit dem GCC-3.3 für die Graphik-Bibliothek VRS getestet.
VRS enthält viele als inline deklarierte Methoden (unter Umständen etwas willkürlich). Das sind
die Ergebnisse in Hinblick auf Größe der Bibliothek und Laufzeiten für einige Beispielprogramme
(Testsystem ist ein Pentium III mit 800Mhz und einer GeForce 3 Grafikkarte unter Linux):
Standard-Inlining Heuristik | Alle Methoden inlinen | |
Größe der Bibliothek | 13320 Kb | 13988 Kb |
Performance gluttorus | 201 fps | 215 fps |
Performance glutdemo/box | 267 fps | 284 fps |
Performance glutdemo/torus | 417 fps | 452 fps |
Performance glutdemo/polygonset | 523 fps | 560 fps |
Performance glutdemo/heightfield | 17.0 fps | 18.7 fps |
Bewertung
Das Verhalten des GCCs beim Inlining ist, zumindest für VRS betrachtet, tatsächlich verbesserungsfähig. Die Standard-Heuristik erzeugt zwar etwa 5% kleineren Code, ist aber in den untersuchten Fällen auch etwa 5 bis 10% langsamer, als wenn alle als inline deklarierten Methoden geinlined werden. Für diesen Geschwindigkeitsfortschritt würde man den größeren Code wohl gerne in Kauf nehmen.
Die Analyse des erzeugten Codes zeigt, dass vor allem VRS-Methoden, die Matrix-Operationen verwenden, im Vergleich zur Standard-Heuristik in der Größe stark zunehmen. Die Methoden zu den Matrix-Manipulation sind also möglicherweise zu großzügig als inline-Methoden deklariert. Durch etwas Fein-Tuning dieser Methoden ließe sich die Größe der kompilierten Bibliothek noch verringern, unter Umständen auch ohne große Geschwindigkeitseinbußen.