Hallo,
ich verwende WinAVR-20090313 mit einem mega128 und den folgenden
Compiler Optionen:
-pedantic
--std=c99
-gdwarf-2
-O3
Folgende Funktion:
Fifo.h
extern inline STD_BOOL bGetFifoBufferEmpty( const struct stFifoBuffer*
const pFifoBuffer );
Fifo.c
STD_BOOL bGetFifoBufferEmpty( const struct stFifoBuffer* const
pFifoBuffer )
{
return pFifoBuffer->m_numBytes == 0;
}
wird in main.c aufgerufen:
#include "Fifo.h"
int main( void )
{
struct stFifoBuffer fifo;
bGetFifoBufferEmpty( &fifo );
return 0;
}
erzeugt den folgenden Code:
<snip>
bGetFifoBufferEmpty( &fifo );
8fa: ce 01 movw r24, r28
8fc: 01 96 adiw r24, 0x01 ; 1
8fe: 0e 94 d2 1b call 0x37a4 ; 0x37a4 <bGetFifoBufferEmpty>
Mir ist bewusst das das inline Schlüsselwort dem Compiler nur einen
Hinweis gibt die Funktionen zu inlinen. Allerdings mache ich in meiner
Andwenung massiv Gebrauch von der Funktion und somit leidet die
Performance erheblich. Welche Möglichkeit habe ich den Compiler zu
zwingen die Funktion zu inlinen ?
Du meinst sicher:
__attribute__((always_inline));
Das zwingt den Compiler aber nur dazu die Funktion zu inlinen wenn keine
Optimierung aktiviert ist ? Hat jedenfalls auch keine Änderung gebracht
:|
static inline ist zwar eine Möglichkeit aber dann muss die Funktion im
Header implementiert werden ?!
Markus Markus schrieb:
> static inline ist zwar eine Möglichkeit aber dann muss die Funktion im> Header implementiert werden ?!
Wenn der Compiler die Implementierung der Funktion nicht kennt, dann
kann er sie nicht inlinen. Egal was du ihm für Tipps gibst, Glaskugel
hat er keine.
Im Regelfall übersetzt ein Compiler ein Quellfile separat vom anderen.
Erst der Linker sieht alles, aber der inlined nichts meher.
Hm, gibt das so nicht später Meckerei vom Linker?
Prinzipiell würde ich vor das 'inline' noch ein 'static' packen, dann
wär es idiotensicher -- was mich zur Frage bringt:
Impliziert das 'inline' schon ein 'static' oder nicht?
Ohne "static" wird der Compiler die Funktion in jedem Fall erzeugen und
den Namen exportieren. Auch wenn er sie vielleicht nie aufruft, weil er
das erst der Linker weiss.
Je nach Art der Übersetzung wird der Linker dann entweder meckern oder
ggf. die redundanten Versionen weglassen (insbesondere bei
-ffunction-sections). Lezteres ist vor allem in C++ ungemein nützlich.
Es ist bei in Includes definierten Inline-Funktionen also i.d.R.
sinnvoll, sie mit "static" zu verzieren.
Sven P. schrieb:
> Hm, gibt das so nicht später Meckerei vom Linker?
Nein. Sollte nicht.
Es ist Aufgabe des Compilers dafür zu sorgen, dass der Linker, sollte er
diese Funktion jemals zu Gesicht kriegen, dies nur einmal tut.
> Prinzipiell würde ich vor das 'inline' noch ein 'static' packen,
Das ist fast immer eine gute Idee.
> Impliziert das 'inline' schon ein 'static' oder nicht?
Nein. static und inline haben so eigentlich nichts miteinander zu tun
und doch hat static Auswirkungen auf inline :-)
Was ist das 'Problem' beim inline?
Selbst wenn der Compiler an einer Stelle entscheidet, dass inlining
angebracht ist, so mus das an anderer Stelle nicht so sein oder z.B.
Compiler-Optionen verhindern inlining. Da der Compiler allerdings
normalerweise nicht alle weiteren Aufrufstellen einer Funktion kennt,
bleibt ihm nichts anderes übrig als die Funktion als nicht-inline
Version ebenfalls anzulegen.
Es gibt nur einen Fall, in dem der Compiler sicher sein kann, alle
Funktionsverwendungen zu kennen und das ist wenn die Funktion static
ist. Hat der Compiler also in einer SourceCode Unit eine static Funktion
und inlined die an allen Stellen, dann kann er sicher sein, dass die
eigentliche Funktion nirgends anders gebraucht wird und braucht daher
keine linkbare Funktion dafür anzulegen. In dem Sinne kann static inline
ein wenig Object Code einsparen.
Karl heinz Buchegger schrieb:
> Es ist Aufgabe des Compilers dafür zu sorgen, dass der Linker, sollte er> diese Funktion jemals zu Gesicht kriegen, dies nur einmal tut.
Bei "extern inline" mitsamt Funktionsrupf im Include-File sind die
Möglichkeiten des Compilers begrenzt und es kann sehr wohl Zoff geben.
Es läuft eher darauf hinaus, dem Linker die Möglichkeit zu geben, sich
aus all den ihm in den Objektfiles präsentierten Implementierungen der
immer gleichen Funktion eine rauszusuchen und die übrigen wegzuwerfen.
Ok, ich habe jetzt verstanden warum der Compiler ohne static Keyword
nicht inlinen kann. Trotzdem stellt sich fuer mich die Frage warum
"extern inline" nicht inlined. Laut Doku:
http://gcc.gnu.org/onlinedocs/gcc/Inline.html:
"...This combination of inline and extern has almost the effect of a
macro. The way to use it is to put a function definition in a header
file with these keywords, and put another copy of the definition
(lacking inline and extern) in a library file. The definition in the
header file will cause most calls to the function to be inlined. If any
uses of the function remain, they will refer to the single copy in the
library...."
sollte das genau meinen gewünschten Effekt erbringen ?! Mit der Option
-Os würde ich verstehen das er trotzdem nicht inlined mit Option -O3
hätte ich es schon erwartet ( wie auch immer die technische Umsetztung
Compiler / Linker intern aussieht )
Markus Markus schrieb:
> Ok, ich habe jetzt verstanden warum der Compiler ohne static Keyword> nicht inlinen kann.
Offensichtlich nicht, denn er kann das sehr wohl.
Bring mal den Quelltest so wie du ihn real dem Compiler zum Frass
vorwirfst. Also nicht ein paar Quellcodezeilen, sondern die Sammlung von
Files. Vielleicht lässt es sich dann leichter erklären.
Markus Markus schrieb:
> Ok, ich habe jetzt verstanden warum der Compiler ohne static Keyword> nicht inlinen kann. Trotzdem stellt sich fuer mich die Frage warum> "extern inline" nicht inlined.
Drehen wir den Spiess mal um.
Du bist jetzt der Compiler. Ich geb dir diesen Code
1
externinlineintfoo();
2
3
voidbar()
4
{
5
i=foo();
6
}
Du sollst diesen Code in C Code umformen, sodass die Funktion foo
geinlined ist. Wie lautet das Ergebnis?
Wenn du zum Schluss kommst, das du das nicht kannst, weil du ja
schliesslich nicht weißt, was in foo() drinnen steht, dann bist du in
genau dem gleichen Dilemma in dem der Compiler bei deiner (identischen)
Konstruktion ist.
Ob die Funktionsdeklaration jetzt direkt da steht, oder ob das über ein
Header File geht, ist ja prinzipiell egal, da ja Header Files vom
Präprozessor aufgelöst werden noch ehe der Compiler den Code zu Gesicht
bekommt.
Markus Markus schrieb:
> Prima, aber er tut es nicht :D Im Anhang das Testprojekt.
Na logisch!
Du hast den Code immer noch nicht im Header File!
Was du ständig geflissentlich ignorierst ist, dass sich der Compiler
jedes *.c File unabhängig von allen anderen vornimmt. Wenn er dein
Hauptfile compiliert, ist er exakt in der Situation, die ich dir 2
Posts weiter oben skizziert habe. Du forderst ihn zum inline auf, gibst
ihm aber nicht die Information die er dafür braucht: nämlich den
Funktionsinhalt!
Wenn du verstanden hast (und das sollte eigentlich nicht schwer sein),
warum in meinem Beispiel weiter oben Du als Compiler den inline nicht
machen kannst, dann solltest du auch verstehen, warum ein richtiger
Compiler das nicht kann. Der hat nämlich auch keine Glaskugel!
> Prima, aber er tut es nicht
Um das nochmal ganz klar zu sagen:
Der Code der Funktion muss beim Übersetzen der jeweiligen C-Datei mit
vorhanden sein, entweder direkt in der C-Datei, oder indirekt über ein
#include, sonst kann der Compiler grundsätzlich nicht Inlinen. Auch
das "extern" sorgt nicht dafür, dass er sich den Code irgendwie auf
magische Weise von woanders herholt.
Karl heinz Buchegger schrieb:
> Du sollst diesen Code in C Code umformen, sodass die Funktion foo> geinlined ist. Wie lautet das Ergebnis?>> Wenn du zum Schluss kommst, das du das nicht kannst, weil du ja> schliesslich nicht weißt, was in foo() drinnen steht, dann bist du in> genau dem gleichen Dilemma in dem der Compiler bei deiner (identischen)> Konstruktion ist.
Mir ist schon klar das der Compiler an dieser Stelle ein Problem hat und
nicht inlinen kann. Trotzdem war meine Vermutung das es Compiler interne
Vorkehrungen gibt ( z.B. erst den gesamten Code zu übersetzen und dann
einen extra Optimierungdurchlauf ) welche dies trotzdem ermöglichen.
Wenn man schaut was die Compiler mittlerweile für interessante
Optimierungsmöglichkeiten haben verwundert es mich umso mehr.
Edit: Ihr seit zu schnell mit euren Antworten :D Das von mir gepostete
Zitat von der GCC Seite sagt doch explicit das extern inline einem Macro
gleichzusetzen ist !
Er sieht nur das hier, sonst nichts. Der Präprozessor hat bereits alle
#define und #include aufgelöst.
Und jetzt verrat mir mal, wie der Compiler, nur mit diesem Quelltext
bewaffnet, die Funktion bGetFifoBufferEmpty inlinen soll?
Markus Markus schrieb:
> Wenn man schaut was die Compiler mittlerweile für interessante> Optimierungsmöglichkeiten haben verwundert es mich umso mehr.
C ist darauf aufgebaut, dass jedes Source Code File unabhängig von
allen anderen compiliert werden kann. Das ist wichtig, weil reale
Projekte gross werden können. Wenn in meinem letzten Projekt alle
C-Source Files compiliert werden müssen, dann dauert das eine Stunde.
Daher ist es wichtig, dass sich der Compiler jedes File einzeln und in
beliebiger Reihenfolge vornehmen kann. Denn dann dauert eine Änderung in
einem File lediglich ein paar Sekunden.
Der Compiler schaut nicht über den Tellerrand! Den interessiert nur der
eine Quelltext, den er zur Zeit bearbeitet
(*) gcc ist da eine Ausnahme. Es gibt eine Option mit der man ihm
mitteilen kann, dass alle in der Commandline angegebenen c-Files das
komplette Programm bilden und abgesehen von diesen Files nichts mehr
dazukommt. Dann kann der gcc auch über diese Files optimieren.
Aber das ist die Ausnahme und nicht die Regel!
Karl heinz Buchegger schrieb:
> Er sieht nur das hier, sonst nichts. Der Präprozessor hat bereits alle> #define und #include aufgelöst.>> Und jetzt verrat mir mal, wie der Compiler, nur mit diesem Quelltext> bewaffnet, die Funktion bGetFifoBufferEmpty inlinen soll?
Gar nicht :) das ist verstanden ! Trotzdem muss es doch eine Möglichkeit
geben den überflüssigen Call zu entfernen. Spätestens der Linker sollte
das merken.
Karl heinz Buchegger schrieb:
> (*) gcc ist da eine Ausnahme. Es gibt eine Option mit der man ihm> mitteilen kann, dass alle in der Commandline angegebenen c-Files das> komplette Programm bilden und abgesehen von diesen Files nichts mehr> dazukommt. Dann kann der gcc auch über diese Files optimieren.> Aber das ist die Ausnahme und nicht die Regel!
Aaaha...genau das mein ich doch ! D.d. der avr gcc unterstüzt diese
Option nicht ? Btw. hast du die Option gerade zur Hand ?
Markus Markus schrieb:
> Karl heinz Buchegger schrieb:>> Er sieht nur das hier, sonst nichts. Der Präprozessor hat bereits alle>> #define und #include aufgelöst.>>>> Und jetzt verrat mir mal, wie der Compiler, nur mit diesem Quelltext>> bewaffnet, die Funktion bGetFifoBufferEmpty inlinen soll?>> Gar nicht :) das ist verstanden ! Trotzdem muss es doch eine Möglichkeit> geben den überflüssigen Call zu entfernen. Spätestens der Linker sollte> das merken.
Das ist im Allgemeinen nicht so einfach.
Es gibt Funktionen, für die es im Quelltext keinen Aufruf gibt und die
trotzdem im kompletten Programm benötigt werden.
Ausserdem ist es nicht Aufgabe des Linkers, Übersetzungsarbeit zu
leisten. Der Compiler hat sich z.B. viel Mühe gegeben eine optimale
Registerverteilung zu erreichen und dann kommt da plötzlich ein Linker
daher und schiebt Code rein und bringt alles durcheinander :-)
Markus Markus schrieb:
> Karl heinz Buchegger schrieb:>>> (*) gcc ist da eine Ausnahme. Es gibt eine Option mit der man ihm>> mitteilen kann, dass alle in der Commandline angegebenen c-Files das>> komplette Programm bilden und abgesehen von diesen Files nichts mehr>> dazukommt. Dann kann der gcc auch über diese Files optimieren.>> Aber das ist die Ausnahme und nicht die Regel!>> Aaaha...genau das mein ich doch ! D.d. der avr gcc unterstüzt diese> Option nicht ? Btw. hast du die Option gerade zur Hand ?
Mach deine inlines einfach so, wie sich die Entwickler von C das
vorgestellt haben.
Du musst das Spiel nach den festgelegten Regeln spielen und nicht die
Regeln passen sich an das an, was du gerne hättest.
Markus Markus schrieb:
> Edit: Ihr seit zu schnell mit euren Antworten :D Das von mir gepostete> Zitat von der GCC Seite sagt doch explicit das extern inline einem Macro> gleichzusetzen ist !
Da steht aber auch:
1
The way to use it is to put a function definition in a header
Markus: Wenn du so programmierst, wie das in C allgemein üblich ist,
dann wird der Compiler dein Freund sein. Naja, meistens jedenfalls.
Wenn du allerdings vom Compiler verlangst, dass er sich deinen Wünschen
anpasst, dann wirst du rasch feststellen, dass Maschinen weitaus sturer
sind all jeder Mensch es sein kann. Diesen Wettbewerb kannst du nicht
gewinnen.
Ach, noch was:
Markus Markus schrieb:
> Gar nicht :) das ist verstanden ! Trotzdem muss es doch eine Möglichkeit> geben den überflüssigen Call zu entfernen. Spätestens der Linker sollte> das merken.
Der Linker kann praktisch nur Adressen ersetzen. Was er auf keinen Fall
kann, ist, den Code an sich zu verändern.
Markus Markus schrieb:
> Edit: Ihr seit zu schnell mit euren Antworten :D Das von mir gepostete> Zitat von der GCC Seite sagt doch explicit das extern inline einem Macro> gleichzusetzen ist !
Der Compiler muss den Quelltext kennen. Punkt.
Dazu gibt es mehrere Wege:
Dir Funktion wird im Header implementierst nebst static inline, siehe
oben. Eine Deklaration ist eine Deklaration. Sie sagt nix über Interna
einer Funktion, die Funktion bleibt eine Black Box wenn sie nur
deklariert wird.
Oder du gibst das Zeug beim Compilieren mit an, etwa so:
Beachte, daß gcc die Quelle foo.c prinzipiell kennen kann, während er
bar.c übersetzt!
Mit folgender Sequenz ist das nicht der Fall. Auch GCC hat noch keine
Kristallkugel.
Karl heinz Buchegger schrieb:
> Markus Markus schrieb:>> Karl heinz Buchegger schrieb:>>> Er sieht nur das hier, sonst nichts. Der Präprozessor hat bereits alle>>> #define und #include aufgelöst.>>>>>> Und jetzt verrat mir mal, wie der Compiler, nur mit diesem Quelltext>>> bewaffnet, die Funktion bGetFifoBufferEmpty inlinen soll?>>>> Gar nicht :) das ist verstanden ! Trotzdem muss es doch eine Möglichkeit>> geben den überflüssigen Call zu entfernen. Spätestens der Linker sollte>> das merken.>> Das ist im Allgemeinen nicht so einfach.> Es gibt Funktionen, für die es im Quelltext keinen Aufruf gibt und die> trotzdem im kompletten Programm benötigt werden.
Stichwort Interrupt Routine.