Inlining beim GCC

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 HeuristikAlle Methoden inlinen
Größe der Bibliothek13320 Kb13988 Kb
Performance gluttorus201 fps215 fps
Performance glutdemo/box267 fps284 fps
Performance glutdemo/torus417 fps452 fps
Performance glutdemo/polygonset523 fps560 fps
Performance glutdemo/heightfield17.0 fps18.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.