Forum: Compiler & IDEs funktionen inlinen


von moop (Gast)


Lesenswert?

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!

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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:
1
#define SET_OUTPUT_HIGH     PORTA |= (1 << 3)
2
#define SET_OUTPUT_LOW      PORTA &= ~(1 << 3)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Du kannst es so verwenden:
1
#include <stdbool.h>
2
#include <avr/io.h>
3
4
static __inline__ __attribute__((always_inline))
5
void setOutput (const bool value)
6
{
7
    if (value)
8
        PORTA |= (1 << 3);
9
    else
10
        PORTA &= ~(1 << 3);
11
}

__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.

von moop (Gast)


Lesenswert?

hi und danke euch!
wenn ich die funktion aber nur in einer Datei benutze dann passt sie 
aber auch in die c-Datei anstatt in den header, richtig?

von Marwin (Gast)


Lesenswert?

Welches Problem versuchst du ueberhaupt zu loesen? Wenn es kein Problem 
ist, dann loese es auch nicht.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Ich hab mir dazu einen Header geschrieben, der alle Pins als 
Bitvariablen definiert. Damit geht es ganz einfach und lesbar:
1
#include "sbit.h"
2
3
#define LED0     PORT_A3
4
#define LED0_oe   DDR_A3
5
6
  LED0_oe = 1; // output
7
  LED0 = 0;    // low
8
  LED0 = 1;    // high

Peter

von moop (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Andreas B. (andreas_b77)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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?

von Peter D. (peda)


Lesenswert?

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

von Fabian O. (xfr)


Lesenswert?

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?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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:
1
-fno-inline-small-functions

Peter

von Fabian O. (xfr)


Lesenswert?

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.

von Masl (Gast)


Lesenswert?

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").

von Peter D. (peda)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.