Ihre Frage ist eine der Fragen, die keine Antwort hat, die man die "absolute Wahrheit" nennen könnte. Der Overhead eines normalen Funktionsaufrufs hängt von drei Faktoren ab:
-
Die CPU. Der Overhead von x86-, PPC- und ARM-CPUs variiert stark, und selbst wenn Sie nur bei einer Architektur bleiben, variiert der Overhead auch ziemlich stark zwischen einem Intel Pentium 4, einem Intel Core 2 Duo und einem Intel Core i7. Der Overhead kann sogar deutlich zwischen einem Intel- und einem AMD-Prozessor variieren, auch wenn beide mit der gleichen Taktfrequenz laufen, da Faktoren wie Cache-Größen, Caching-Algorithmen, Speicherzugriffsmuster und die tatsächliche Hardwareimplementierung des Call-Opcodes selbst einen großen Einfluss auf den Overhead haben können.
-
Die ABI (Application Binary Interface). Selbst bei derselben CPU existieren oft unterschiedliche ABIs, die spezifizieren, wie Parameter bei Funktionsaufrufen übergeben werden (über Register, über Stack oder über eine Kombination aus beidem) und wo und wie die Initialisierung und Aufräumung des Stackrahmens erfolgt. All dies beeinflusst den Overhead. Unterschiedliche Betriebssysteme können unterschiedliche ABIs für dieselbe CPU verwenden; z.B. können Linux, Windows und Solaris alle drei ein anderes ABI für dieselbe CPU verwenden.
-
Der Compiler. Das strikte Befolgen der ABI ist nur wichtig, wenn Funktionen zwischen unabhängigen Codeeinheiten aufgerufen werden, z.B. wenn eine Anwendung eine Funktion einer Systembibliothek aufruft oder eine Benutzerbibliothek eine Funktion einer anderen Benutzerbibliothek aufruft. Solange Funktionen "privat" sind und außerhalb einer bestimmten Bibliothek oder eines bestimmten Binärdatenträgers nicht sichtbar sind, kann der Compiler "tricksen". Er muss sich nicht strikt an die ABI halten, sondern kann Abkürzungen verwenden, die zu schnelleren Funktionsaufrufen führen. Er kann z.B. Parameter in einem Register übergeben anstatt den Stack zu verwenden oder er kann das Einrichten und Aufräumen des Stackrahmens komplett überspringen, wenn es wirklich nicht notwendig ist.
Wenn Sie den Overhead für eine bestimmte Kombination der drei oben genannten Faktoren, z.B. für Intel Core i5 unter Linux mit GCC, wissen möchten, ist Ihr einziger Weg, diese Informationen zu erhalten, das Benchmarking des Unterschieds zwischen zwei Implementierungen, einer mit Funktionsaufrufen und einer, bei der Sie den Code direkt in den Aufrufer kopieren; auf diese Weise erzwingen Sie definitiv das Inline-Einfügen, da die Inline-Anweisung nur ein Hinweis ist und nicht immer zum Inline-Einfügen führt.
Die eigentliche Frage hier ist jedoch: Spielt der genaue Overhead wirklich eine Rolle? Eines ist sicher: Ein Funktionsaufruf hat immer einen Overhead. Er kann klein sein, er kann groß sein, aber er existiert auf jeden Fall. Und egal wie klein er ist, wenn eine Funktion in einem leistungsentscheidenden Abschnitt oft aufgerufen wird, wird der Overhead in gewissem Maße eine Rolle spielen. Das Inline-Einfügen macht Ihren Code selten langsamer, es wird ihn jedoch größer machen. Die heutigen Compiler sind ziemlich gut darin, selbst zu entscheiden, wann sie inline einfügen und wann nicht, sodass Sie sich kaum Gedanken darüber machen müssen.
Persönlich ignoriere ich das Inline-Einfügen während der Entwicklung vollständig, bis ich ein mehr oder weniger brauchbares Produkt habe, das ich profilieren kann, und nur wenn das Profiling mir sagt, dass eine bestimmte Funktion wirklich oft aufgerufen wird und auch innerhalb eines leistungsentscheidenden Abschnitts der Anwendung, werde ich das "erzwungene Inline-Einfügen" dieser Funktion in Betracht ziehen.
Bisher ist meine Antwort sehr allgemein, sie gilt sowohl für C als auch für C++ und Objective-C. Als Abschluss möchte ich noch etwas zu C++ sagen: Methoden, die virtuell sind, sind doppelte indirekte Funktionsaufrufe, das bedeutet, sie haben einen höheren Funktionsaufruf-Overhead als normale Funktionsaufrufe und sie können auch nicht eingefügt werden. Nichtvirtuelle Methoden können vom Compiler eingefügt werden oder nicht, aber auch wenn sie nicht eingefügt werden, sind sie immer noch signifikant schneller als virtuelle. Sie sollten also Methoden nicht virtuell machen, es sei denn, Sie planen wirklich, sie zu überschreiben oder haben vor, sie überschreiben zu lassen.
0 Stimmen
Voting to close as "zu breit". Dies kann nicht wirklich sinnvoll beantwortet werden. Es hängt vom Compiler, der Hotspot-Erkennung, der CPU (Caching, spekulativer Ausführung, Branch-Ziel-Pufferung) und vielen weiteren Faktoren ab.