hi,
ich muss innerhalb einer c-Datei öfter mal einen bestimmten Ausgang
setzen oder löschen. (immer der gleiche, PORTA Pin3)
Ich will nur nicht an mehreren Stellen den Registerzugriff haben und
Bits herumschieben, deswegen würde ich gerne eine kleine Funktion dafür
verwenden.
void setOutput(bool value)
{
if (value == TRUE)
PORTA |= (1 << 3);
else
PORTA &= ~(1 << 3);
}
eigentlich ist doch ein Funktionsaufruf overkill für so eine simple
Aufgabe.
aber der Compiler kann doch das inlinen oder?
und die if-abfrage kann er ja auch rausschmeissen?
so dass letztendlich nur dasteht: PORTA |= (1 << 3);
Macht er das immer? Oder sollte ich static inline dazuschreiben?
ich versteh den assembler code leider nicht drum kann ich nicht
nachschaun was der Compiler draus macht :-(
danke schon mal!
moop schrieb:> eigentlich ist doch ein Funktionsaufruf overkill für so eine simple> Aufgabe.
Ja.
> aber der Compiler kann doch das inlinen oder?
Unter bestimmten Voraussetzungen: Ja. Zum Beispiel wäre es vorteilhaft,
die Funktion static zu definieren. Dann weiß der Compiler, dass sie
ausschließlich aus diesem C-Modul aufgerufen wird.
> und die if-abfrage kann er ja auch rausschmeissen?
Kommt drauf an: Wenn Du die Funktion mit einem konstanten Wert aufrufst,
ja. Wenn Du sie aber mit einer Variablen aufrufst, die erst at runtime
einen Wert erhält, kann der Compiler das nicht.
> Macht er das immer? Oder sollte ich static inline dazuschreiben?
Standardmässig sollte er so eine kleine Funktion inlinen. Wenn Du sie
noch static machst, sollte das reichen.
Eine Alternative wäre der Einsatz des Preprocessors:
__inline__ anstatt inline geht auch mit C90; wenn du C99 verwendest
geht auch inline.
Das always_inline inlint auch ohne Optimierung, und in dem Fall wird das
const gebraucht um den Vergleich rauszuwerfen.
Explizit genen true zu vergleichen ist übel, weil alles != false als
true genommen wird.
Bevorzugt steht so eine Funktion in einem Header.
wie in meinem ersten Post geschrieben:
ein Pin-Setzen oder -Löschen zentral an einer Stelle zu machen anstatt
den Portzugriff und das Geshifte an mehreren Stellen im Code zu haben.
Aber möglichst so, das der resultierende Code genauso performant ist.
Und nebenbei noch was über den Compiler zu lernen.
moop schrieb:> das der resultierende Code genauso performant ist.
Ob ausgeschrieben, Inline oder Macro, der Code ist immer gleich groß.
Der Compiler ersetzt das konstante Shift durch SBI/CBI.
Compiler sind recht gut im Ausrechnen konstanter Ausdrücke. Sie rechnen
lieber gleich, anstatt Laufzeitcode zu erzeugen.
Peter
Johann L. schrieb:> Das always_inline inlint auch ohne Optimierung, und in dem Fall wird das> const gebraucht um den Vergleich rauszuwerfen.
Unnötig. Wenn das als normale Funktion aufgerufen wird, muss dort
verglichen werden ob du nun const schreibst oder nicht. Wenn es inline
expandiert wird, erkennt der Compiler, dass der Wert von value einen
bekannten Wert hat und optimiert den Vergleich weg, const oder nicht.
Andreas B. schrieb:> Johann L. schrieb:>> Das always_inline inlint auch ohne Optimierung, und in dem Fall wird das>> const gebraucht um den Vergleich rauszuwerfen.>> Unnötig. Wenn das als normale Funktion aufgerufen wird, muss dort> verglichen werden ob du nun const schreibst oder nicht. Wenn es inline> expandiert wird, erkennt der Compiler, dass der Wert von value einen> bekannten Wert hat und optimiert den Vergleich weg, const oder nicht.
Bitte überprüfe deine Behauptung! (Hab ich zB gemacht, bevor ich meinen
Beitrag verfasst hab).
Du behauptest also, daß mit -O0 der gleiche Code erzeugt wird, egal ob
da ein const steht oder nicht?
Johann L. schrieb:> Du behauptest also, daß mit -O0 der gleiche Code erzeugt wird
Immer diese praxisfernen Annahmen.
Default ist -Os und das aus gutem Grund.
Wer -O0 benutzt, dem kommts auch nicht mehr auf die paar Byte mehr an,
dem sind 500% Overhead egal.
Peter
Macht es bei -Os eigentlich einen Unterschied ob man eine
static-Funktion mit "inline" kennzeichnet oder nicht?
Theoretisch sollte durch -Os ja immer die Variante mit kleinstmöglichem
Code erzeugt werden? Oder werden explizit gekennzeichnete
Inline-Funktionen (also nur mit "inline", nicht "always_inline") auch
geinlint, wenn der Code dadurch ein paar Bytes größer, aber dafür
schneller wird?
Peter Dannegger schrieb:> Johann L. schrieb:>> Du behauptest also, daß mit -O0 der gleiche Code erzeugt wird>> Immer diese praxisfernen Annahmen.> Default ist -Os und das aus gutem Grund.
In GCC ist -O0 der Default. Aber darum geht es hier garnicht. Sondern
darum, daß unüberlegt und grundlos offenbar falsche Aussagen bemacht
werden.
Johann L. schrieb:> In GCC ist -O0 der Default. Aber darum geht es hier garnicht. Sondern> darum, daß unüberlegt und grundlos offenbar falsche Aussagen bemacht> werden.
Ja. ok.
Momentan ist das beim gcc eben so.
Allerdings erschliesst sich mir aus Sicht des Sprachstandards nicht,
warum das so sein sollte.
Das irgendmal wer entschieden hat, diese Optimierung in -O0 nur bei
explizit const markierten Argumenten zu machen, ist zwar ein Faktum. Nur
sehe ich ehrlich gesagt keinen Grund, warum das so sein muss.
> -O0
Na ja. Der sinnvolle Default in der AVR-Programmierung ist nun mal -Os
und nicht -O0. Von daher dürften wohl die wenigsten jemals -O0 benutzen.
Von daher: sei ein wenig nachsichtig, wenn wir nicht für alle
Optimierungen auswendig wissen, welche Optimierung unter welchen
Nebenbedingung durchgeführt wird und wann nicht. Manche Optimierungen
sind so trivial, dass man wohl davon ausgeht, dass der Compiler sie
praktisch immer machen wird.
Fabian O. schrieb:> Theoretisch sollte durch -Os ja immer die Variante mit kleinstmöglichem> Code erzeugt werden?
Grau ist alle Theorie.
Fabian O. schrieb:> Oder werden explizit gekennzeichnete> Inline-Funktionen (also nur mit "inline", nicht "always_inline") auch> geinlint
Nicht nur die, sondern auch alle, die der Compiler als kurz einschätzt
(ohne daß sie es wirklich sind).
Daher sollte man das ungefragte Inlinen explizit verbieten, was nochmal
drastisch kleineren Code ergibt:
Das hatten wir schon mal, ich bin da anderer Meinung. ;-)
Ich möchte genau dieses Inlinen von "kleinen" Funktionen gerne haben,
denn ich gliedere meinen Code gerne in kurze Unterfunktionen, um
bestimmten Aktionen Namen zu geben, selbst wenn sie zum Teil nur aus
einer Anweisung bestehen oder nur von einer Stelle aus aufgerufen
werden.
Solche Dinge wie Hardwarekonfiguration und Registerzugriffe wären ein
Paradebeispiel. Wenn man sie als "static inline"-Funktion in ein
Headerfile auslagert, erhält man schön lesbaren und hardwareunabhängigen
Code.
Dafür möchte ich aber nicht mit langsameren Code bestraft werden. Wenn
inline-Funktionen als auch geinlint werden, obwohl es mit einem echten
Funktionsaufruf noch etwas kleiner ginge, fände ich daher nicht schlimm.
Ich bin aber auch einverstanden, wenn der GCC -Os als "so klein wie nur
irgendwie geht, Geschwindigkeit und Stacknutzung völlig egal"
interpretiert. Mich würde nur interessieren, wie es tatsächlich ist.
Benutzt man -fno-inline-small-functions nicht eher deswegen weil der
Compiler nicht weiß ob die Funktion 2, 10 oder 1000 mal geinlined werden
muss?
Ich denke den Code einer Funktion kann er recht gut abschätzen, er
erzeugt ihn ja (Vorrausgesetzt, du und der Compiler (bzw. dessen
Programmierer) habt die selbe Definition von "klein").
Fabian O. schrieb:> Mich würde nur interessieren, wie es tatsächlich ist.
Ganz einfach:
1.
-fno-inline-small-functions
-> kein heimliches inline
2.
static inline
-> meistens inline (ich hatte schonmal den Fall, wo es ignoriert wurde)
3.
_attribute_ ((always_inline))
-> immer inline
4.
globale Optimierung über alle Sourcen:
-> alle nur einmal aufgerufenen Funktionen inline
5.
_attribute_ ((noinline))
-> niemals inline
Die Zahlen 1..5 geben die Rangfolge an (5 = höchste).
Peter