inline

Inhaltsverzeichnis[Anzeigen]

Dank inline kann der Compiler einen Funktionsaufruf durch seinen Funktionskörper ersetzen. Zwei Gründe sprechen für das Verwenden von inline-Funktionen. Performanz und Sicherheit.

 

Eigentlich habe ich mir vorgenommen, mir in diesem Beitrag nur die Performanzbrille aufzusetzen. Gott sei Dank, habe ich noch rechtzeitig meine Scheuklappen abgelegt. Ein großer Vorteil von inline-Funktionen ist es, dass durch sie Makros als Funktionsersatz überflüssig werden.

Makro must go

Makros stellen nur ein einfaches Mittel zur Textsubstitution dar. Makros besitzen kein Verständnis von der C++-Syntax. Daher kann viel schief gehen.

 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// macro.cpp

#include <iostream>

#define  absMacro(i) ( (i) >= 0 ? (i) : -(i) )

inline int absFunction(int i){
  return i >= 0 ? i : -i;
}

int func(){ 
  std::cout << "func called" << std::endl;
  return 0;
}


int main(){
  
  std::cout << std::endl;
  
  auto i(0);
  auto res = absMacro(++i);   
  std::cout << "res: " << res << std::endl;
  absMacro(func());
  
  std::cout << std::endl;
  
  i=0;
  res= absFunction(++i);     
  std::cout << "res: " << res << std::endl;
  absFunction(func());
  
  std::cout << std::endl;
  
}

 

inline

Sowohl das Makro in Zeile 5 als auch die inline-Funktion in Zeile 5 geben den absoluten Wert ihres Arguments zurück. Im konkreten Fall rufe ich die Funktionen mit ++i auf. i ist in dem konkreten Fall 0. Das Ergebnis sollte daher 1 sein. Sollte. Den in dem Makro wird der Ausdruck i zweimal inkrementiert. Konsequenterweise ist das Ergebnis 2 anstelle von 1. Schön zeigt dies die Funktion func. Verwende ich diese als Argument, wird sie in dem Makro zwei-, in der inline-Funktion einmal ausgeführt.

Welche Automatismen finden bei inline-Funktionen statt?

inline

Zuerst einmal, ist nicht alles so einfach, wie es scheint. Wird eine Funktionsdefinition mit dem Schlüsselwort inline versehen, ist das für den Compiler nur eine Empfehlung. Ihm steht es frei, den Funktionsaufruf direkt durch den Funktionskörper zu ersetzen. Es geht aber auch anders. Moderne Compiler wie der Microsoft Visual C++ - , der gcc- oder der clang-Compiler inlinen eine Funktion auch automatisch, falls es aus Optimierungsgründen angesagt ist.

Nun muss ich im Konjunktiv argumentieren. Angenommen, der Compiler folgt meiner Aufforderung und setzte das inline-Schlüsselwort in der exchange-Funktion um. 

inline void exchange(int& x, int& y){
  int temp= x;
  x= y;
  y= temp;
}

 

Was passiert nun bei einem Funktionsaufruf?

 

...
auto a(2011);
auto b(2014);
exchange(a,b);
...

 

Der Compiler ersetzt den Funktionsaufruf durch seinen Funktionskörper.

...
auto a(2011);
auto b(2014);
int temp= a;
a= b;
b= temp;
...

 

An dem kleinen Beispiel lassen sich schön die Vor- und Nachteile der inline-Funktionen festmachen.

Vorteile

  • Kein Funktionsaufruf ist notwendig.
  • Variablen müssen nicht auf den Funktionsstack geschoben bzw. wieder von ihm entfernt werden.
  • Die Funktion benötigt keinen Rückgabewert.
  • Der Befehlscache kann optimaler verwendet werden. Neue Befehle müssen nicht umständlich geladen, sondern können sofort ausgeführt werden.

Nachteile

  • Die Größe der ausführbaren Datei wächst.

Auch wenn ich hier nur ein Nachteil genannt habe, soll dies keine Wertung sein. Im Wesentlichen ist die Verwendung des Schlüsselworts inline eine Abwägung von Performanz versus Größe der ausführbaren Datei. Das war die einfache Richtschnur. Im Detail ist die Abwägung deutlich diffiziler. So kann die ausführbare Datei durch inline schneller oder langsamer, größer oder kleiner sein werden. inline kann den Absturz des Programms verursachen oder verhindern oder auch die Anzahl der Cache-Misses erhöhen oder reduzieren. Wer sich verwirren lassen will, den verweise ich gerne auf die FAQ zu inline-Funktionen auf isocpp.org

Bisher habe ich inline nur im Zusammenhang mit Funktionen beschrieben. Natürlich lassen sich Methoden auch inline definieren.

inline-Methoden

Eine Methode wird implizit und explizit zur inline-Methode. Implizit wird eine Methode zur inline-Methode, wenn sie im Klassenkörper definiert wird. Explizit, wenn sie außerhalb der Klasse definiert und mit dem Schlüsselwort inline versehen wird.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// inline.cpp

class MyClass{
public:
  void implicitInline(){};
  void explicitInline();
  inline void notInline();
};

inline void MyClass::explicitInline(){}
void MyClass::notInline(){}

int main(){

  MyClass cl;
  cl.implicitInline();
  cl.explicitInline();
  cl.notInline();

}

 

So ist die Methode implicitInline in Zeile 5 inline, da sie im Klassenkörper definiert wird. So ist die Methode explicitInline in Zeile 6 inline, da bei ihrer Definition das Schlüsselwort inline angewandt wird. Ich will gerne explizit betonen. Komm nur bei der Methodendeklaration das Wort inline zum Einsatz, besitzt dies keine Auswirkung. Genau dieser Fehler ist mir bei der Methode notInline in Zeile 7 unterlaufen.

Allgemeine Empfehlung

Guter Rat ist teuer. Soll das Schlüsselwort inline immer oder nie verwendet werden? So einfach kann ich es mir natürlich nicht machen. Die Verwendung von inline ist dann angesagt, wenn eine Funktion einen kleinen Funktionskörper besitzt, zeitkritisch ist und nicht allzu häufig aufgerufen wird. Damit überwiegen die Performanzvorteile der ausführbaren Datei häufig die Nachteile, die durch eine größere ausführbare Datei entstehen.

Um das große Bild nicht zu verlieren. 2006 verfasste die Working Group WG 21 den ISO/IEC TR 18015 zur C++ Performanz. In dem Abschnitt 5.3.4 gehen sie explizit auf das Schlüsselwort inline für fünf populäre C++-Compiler ein. In ihrem Vergleich berücksichtigen sie Funktion, inline-Funktionen und Makros. Mein Fazit aus ihrer Performanzmessung ist es, dass einerseits inline-Funktionsaufruf um den Faktor 2 - 17 schneller sind als nicht inline-Funktionen und das andererseits Makros und inline-Funktionen in der gleichen Performanzliga spielen.

Wem eine einfache Fausformel nicht reicht, der kommt am Profilieren seines ausführbaren Programms nicht vorbei. Dies gilt natürlich insbesondere für embedded Systeme, in den Speicher typischerweise eine kritische Ressource ist.

Nachdem ich einige Aufmerksamkeit auf Reddit erfahren habe, da ich den wichtigsten Punkte zu inline Funktionen vergessen habe, hier ein paar Wort zu ORD.

ODR 

ODR steht für One Definition Rule und sagt im Falle einer Funktion aus.

  • Eine Funktion kann nicht mehr als eine Definition pro Übersetzungseinheit besitzen.
  • Eine Funktion kann nicht mehr als eine Definition pro Programm besitzen.
  • Eine inline Funktionen mit external linkage kann in mehr als einer Übersetzungseinheit definiert werden. Für jede Definition muss aber gelten, dass sie identisch ist.

In modernen Compilern besitzt das Schlüsselwort inline nicht mehr die Funktionalität, Funktionen zu inlinen. Moderene Compiler ignorieren es fast nahezu. Der Anwendungsfall für inline besteht darin, Funktionen auszuzeichnen, die gemäß der ODR Regel richtig sind. Der Name inline ist mittlerweile sehr irreführend.

C versus C++

Sorry, aber die Verwirrung geht weiter. Ich will einen Punkt explizit betonen. inline Funktionen besitzen per default external linkage in C++. Das gilt aber nicht für C. inline Funktionen besitzen per default internal linkage. Die Details dazu kannst du schön in dem Artikel Linkage of inline functions nachlesen.

Wie geht's weiter?

Nun verlassen ich klassisches C++ und wende mich wieder modernem C++ zu. In C++11 wurde das Schlüsselwort constexpr eingeführt. Damit lassen sich Werte, Funktionen und eigene Datentypen definieren, die zur Compilezeit evaluiert werden können. Alles, was zur Compilezeit ausgeführt wird, bietet deutliche Vorteile. Welche, das zeigt der nächste Artikel.

 

 

 

 

 

 

title page smalltitle page small Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library".   Hole dir dein E-Book. Unterstütze meinen Blog.

 

 

Tags: inline

Kommentare   

0 #1 Benjamin Bannier 2016-01-17 09:49
Was für ein seltsamer Artikel: einerseits allerhand Verweise auf Performance-Vorteile, andererseits (berechtigte) Einwände, dass nichts davon garantiert sei.

Was für mich eindeutig fehlt, ist eine Erklärung, wofür "inline" denn nun eigentlich gedacht ist (Klärung von potentiellen ODR-Verletzungen). Alles andere ist nett, aber meilenweit davon entfernt irgendwie garantiert zu sein. Sie verweisen zwar auf TR18015; allerdings ist dieser Artikel nun bald 10 Jahre alt, und bereits damals haben einige Compiler Inlining ohne extra Auszeichnungen durchgeführt. Da dort ja das komplette Benchmark beschrieben ist, hätten hier aktuelle Messungen einiges zu einer sinnvolleren Diskussion beitragen können (ich kann übrigens mit meinem Haus-Compiler keine Performance-Vorteile durch "inline" für die Beispiele messen).

Alles in allem ein leider weniger nützlicher Artikel.
Zitieren
0 #2 Rainer Grimm 2016-01-19 07:01
zitiere Benjamin Bannier:
Was für ein seltsamer Artikel: einerseits allerhand Verweise auf Performance-Vorteile, andererseits (berechtigte) Einwände, dass nichts davon garantiert sei.

Genau das war die Intention meines Artikels. Zum einen wollte ich die Mechanismen hinter inline beschreiben, zum anderen zeigen, dass nichts garantiert ist. Meine Intention war aber nicht, Code zu liefern, der die Performanzvorteile von inline-Funktionen unter Beweis stellt.
Meine zentrale Aussage ist, dass erst nach dem Profilieren des Programms endgültige Aussagen möglich sind. Danach gilt es natürlich, Performanz versus Objektgröße abzuwägen.
Zitieren

Kommentar schreiben


Modernes C++

Abonniere den Newsletter

Inklusive zwei Artikel meines Buchs
Introduction und Multithreading

Beiträge-Archiv

Sourcecode

Neuste Kommentare