Forum: Compiler & IDEs Inline Funktionen und Codeoptimierung


von Malte (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
wenn ich in einer Funktion den direkten Zugriff auf ein Array durch
eine static inline Funktion ersetze, so erhöht sich aus welchen Gründen
auch immer der benötigte Speicherplatz.

#define pixel_set_always_safe 0

static _inline_ void pixel_set(u08 posx, u08 posy, u08 color) {
#if (pixel_set_always_safe == 1)
if ((posx < screenx) && (posy < screeny)) {
  gdata[posy][posx] = color;
}
#else
  gdata[posy][posx] = color;
#endif
}

void move_line_up (u08 x) { //Zeile um 1 Pixel nach oben
u08 y;
for (y = 0;y < (screeny-1);y++) {
  pixel_set(x,y,gdata[y+1][x]);
  //gdata[y][x] = gdata[y+1][x];
  if (y == (screeny-2)) {
    pixel_set(x,y+1,linebuff[x]);
    //gdata[y+1][x] = linebuff[x];
  }
}
}

Direkt dach Eintritt in die for-Schleife ist es für die Code Größe
egal, ob ich pixel_set oder direkt gdata verwende. Innerhalb der
if-Verzeweigung kostet das Verwenden von pixel_set Anstelle von gdata
jedoch 12 Byte Code. Den generierten Assembler Code von beiden
Varianten habe ich mal als Datei angehängt.
Was mus ich verändern, sodass der generierte Code bei Verwendung der
Inline-Funktion genauso klein bleibt?
Schon mal vielen Dank im Voraus.

Vielleicht noch nützliche Informationen:
typedef uint8_t  u08;
#define screenx 16
#define screeny 16
u08 volatile gdata[screeny][screenx];
avr-gcc Version: 3.4.1
Compiler Optimierungen:
OPT = -O2 -ffast-math -fweb -frename-registers -Winline -mtiny-stack

von Alex (Gast)


Lesenswert?

In meinen Augen heißt inline, dass der Compiler die so deklarierte
Funktion überall wo sie aufgerufen wird, komplett einfügt. Es wird kein
Funktionsblock draus, der bei jedem Aufruf angesprungen wird.
Somit wird jeder Aufruf dieser Funktion deinen Code weiter vergrößern,
was ja logisch ist.

Alex

von Chris (Gast)


Lesenswert?

Alex: Du hast zwar Recht, aber das meint Malte nicht. Die
inline-Funktion besteht nur aus einer einzigen Zeile. Ob man die nun
manuell per Copy&Paste in den Code schreibt oder das dem Compiler (über
inline) erledigen lässt, sollte keine Rolle spielen.


Malte: Es wäre u.U. hilfreich, ein Listing zu posten, bei dem
abwechselnd C-Code/Assembler-Code enthalten ist, damit man die Zeilen
leichter zuordnen kann.
Sollte eigentlich nichts ausmachen, aber lass mal das static vor der
Funktion weg. Nimm lieber einen unnamed namespace, wenn du
Namenskollisionen vermeiden willst/musst.

Ich möchte hier keine Mutmaßungen anstellen, aber evtl. ist das ein Bug
des gcc. Der 3.4.x hat nämlich mit dem AVR-target noch ein paar
Probleme. Siehe z.B. auch:
http://lists.gnu.org/archive/html/avr-libc-dev/2004-11/msg00086.html

Dort generiert der 3.4.3er gigantischen Code für eine simple
if-Abfrage.

Allerdings würde ich wirklich sicher gehen, dass es ein Bug ist, bevor
du in die gcc-Bugdatenbank postet. Z.B. Gegenüberstellung des
generierten Codes von gcc 3.4.x <-> 3.3.x, wie in dem Link oben.

von Malte (Gast)


Lesenswert?

Bezüglich der Codegröße und möglichen Bugs:
Mir ist aufgefallen, dass ein und das selbe Projekt mit
unerschiedlichen ausgewählten MCUs erhebliche Code-Größenschwankungen
unterliegt.
     in Byte ohne -mint8; mit -mint8
ATMEGA16/32 :   7326       7332
ATMEGA8     :   6834       6830
AT90S8535   :   7182       7138
Eigentlich müsste der Code mit mint8 jedesmal kleiner oder gleichgroß
sein. Dass der Code für den MEGA8 kleiner ist als für den 90S8535 lässt
sich wohl mit dem erweitertem Befehlssatz (insbesondere movw) erklären.
Aber warum der Code für den MEGA32 so groß ist verstehe ich nicht (die
unterschiedlich großen Interrupt Tabelle können nich so viel
ausmachen).

Die Assembler Ausgabe kann ich bei Gelegenheit mal mit dem C-Code
versehen (aber nicht mehr heute). Wie ich mit verschiedenen Versionen
von avr-gcc gleichzeitig arbeite weiß ich leider nicht genau, da ich
avr-gcc einfach per apt-get (Debian testing) installiert habe.
Vielleicht hilf ja, dass
avr-gcc -dumpversion
3.4.1 ausgibt und
gcc -dumpversion
3.3.4

von Malte (Gast)


Lesenswert?

PS: wenn ich das static weg nehme, will avr-gcc nicht Compilieren.
graphicfunctions.h:22: multiple definition of `pixel_set'
lautet die Fehlermeldung. Die kann aber auch daran liegen, dass die
Header Datei in mehreren .c Dateien meines Projekts verwendet wird.

von Chris (Gast)


Lesenswert?

> PS: wenn ich das static weg nehme, will avr-gcc nicht Compilieren.
> graphicfunctions.h:22: multiple definition of `pixel_set'
> lautet die Fehlermeldung.

Ah ja. Ich sehe gerade, du hast inline geschrieben. Probier mal bitte
nur inline, ohne die Unterstriche.
Eigentlich sollte inline genau diese Fehlermeldung beheben (das
inline-Schlüsselwort macht nämlich mehr als nur die Funktion zu
inlinen).

von Jörg Wunsch (Gast)


Lesenswert?

Coedgrößenschwankungen:

AT90S8515 ist noch der alte CPU-Kern, der konnte eine ganze Menge an
Befehlen nicht.  Daraus folgt, daß der Code größer wird.

ATmega8 hat einen neuen Kern und kommt komplett mit RJMP/RCALL aus, da
diese exakt 8 KB ROM adressieren können.  Das ergibt den kleinsten
Code in Deinem Vergleich.

ATmega16+ hat zwar auch einen neuen Kern, aber durch den ROM > 8 KB
lassen müssen absolute JMP/CALL Befehle genommen werden, um alles zu
erreichen, daher wird der Code größer.  (Eine Schachtelung, um
möglichst weitgehend mit RJMP/RCALL arbeiten zu können, unterstützt
der GCC bislang nicht.)

von Malte (Gast)


Angehängte Dateien:

Lesenswert?

@Jörg Wunsch:
Danke für die Erklärumg. Vielleicht wäre das ja noch eine Ergänzung für
die FAQ der avr-libc Doku. :-)

@Chris:
Von der Fehlermeldung her mach es keinen Unterschied ob ich _inline_
oder nur inline schreibe.
static inline void pixel_set(...: geht
statich _inline_ void pixel_set(...: geht
inline void pixel_set(...: Fehler wie oben beschrieben
_inline_ void pixel_set(...: Fehler wie oben beschrieben
void pixel_set( : Fehler wie oben beschrieben

Wie schon gesagt, ich vermute das liegt daran, dass die Funktion in
einer Header Datei steht, die von mehreren .c Dateien eingebunden wird.
Ich habe die komplette Compiler Meldung mal als Anhang hinzugefügt,
falls Interesse besteht.

von Jörg Wunsch (Gast)


Lesenswert?

Ja, die Definition einer Funktion darf nicht in einer Headerdatei
stehen, daran ändert auch _inline_ nichts.  Der Compiler generiert
nämlich dennoch eine Kopie der Funktion außerhalb allen inline-Codes,
da in C _inline_ ja nur ein Hinweis und keine zwingende Forderung
ist, so daß andere Teile eben diese global sichtbare Funktion
gebrauchen könnten.  Wenn Du nun die Funktion derart in mehreren
Quelldateien generiert hast, bekommst Du genau diesen Fehler.

static _inline_ ist die korrekte Verfahrensweise: der Compiler weiß
damit, daß die Funktion nur innerhalb dieses Übersetzungsmoduls
benötigt wird, so daß er entweder eine lokale nicht inline aufgelöste
Kopie der Funktion generieren kann oder aber tatsächlich allen Code
inline auflösen konnte und auf die globale Funktion gänzlich
verzichten darf.

von Malte (Gast)


Angehängte Dateien:

Lesenswert?

So, ich habe den Code jetzt mal mit und ohne Verwendung von Inline in
einer html Tabelle gegenübergestellt (siehe Anhang).

Bezüglich der Gegenüberstellung des mit verschiedenen Compilerversionen
generierten Codes:
Leider habe ich keine Ahnung wie ich eine andere avr-gcc Version
installiere (Debian-Linux). Ich finde außerdem nur die Möglichkeiten
zum Download von  gcc, nich aber avr-gcc. Wenn ich versuche mit gcc zu
compilieren kennt dieser den -mmcu Parameter nicht.

von Chris (Gast)


Lesenswert?

@Jörg:
> Ja, die Definition einer Funktion darf nicht in einer
> Headerdatei stehen, daran ändert auch _inline_ nichts.

Ich widerspreche dir nur ungern, aber in C++ ist inline mehr als ein
Vorschlag an den Compiler, die Funktion zu inlinen.
Aus dem C++-Standard (3.2.3, Auszug):
"An inline function shall be defined in every translation unit in
which it is used."
(7.1.2 sagt das gleiche, fügt aber noch die Bedinung hinzu, dass alle
Definitionen exakt gleich sein müssen).

Das sagt ziemlich eindeutig, dass eine inline-Funktion in einer
.h-Datei definiert (und nicht nur deklariert) werden sollte.


Ich kenne mich mit C nicht so gut aus, womöglich ist es dort doch
anders als in C++.

@Malte:
Lad dir den normalen gcc-Quellcode runter, falls du es nicht schon
hast. Bei configure kannst du dann als target avr angeben, siehe:
http://www.nongnu.org/avr-libc/user-manual/install_tools.html#install_avr_gcc

von Jörg Wunsch (Gast)


Lesenswert?

> Ich widerspreche dir nur ungern, aber in C++ ist inline mehr als ein
> Vorschlag an den Compiler, die Funktion zu inlinen.

In C schon.  Einer der Unterschiede zwischen C und C++.  In der Tat
überlegt sich der GCC in C mehr als einmal, ob er Deiner Idee von
inline wirklich folgen wird.  (Es gibt noch irgendein GCC
_attribute_, mit dem man das Inline erzwingen kann, aber den Namen
habe ich vergessen.)

von Chris (Gast)


Lesenswert?

> Einer der Unterschiede zwischen C und C++.

Dann hast du natürlich Recht.
Diese kleinen aber feinen Unterschiede zwischen C und C++ sind mir
leider noch nicht alle bekannt.

von Malte (Gast)


Lesenswert?

@Chris:
Die Anleitung war genau das was ich suchte, danke.
Allerdings bricht make nach einer ganzen Weile des Compilierens mit
einigen Fehlern ab (siehe unten). Davor gabs auch schon eine Menge
Warnungen. Versucht hab ich es mit gcc 3.3.4. Auf meinem System war
bereits avr-libc (1.0.4) und binutils (2.15) installiert.
So langsam wird mir das alles etwas zu kompliziert. :-(

../../gcc/config/avr/libgcc.S: Assembler messages:
../../gcc/config/avr/libgcc.S:72: Error: suffix or operands invalid for
`clr'
../../gcc/config/avr/libgcc.S:72: Error: no such instruction: `clear
result'
../../gcc/config/avr/libgcc.S:74: Error: no such instruction: `sbrc
r24,0'
../../gcc/config/avr/libgcc.S:75: Error: too many memory references for
`add'
../../gcc/config/avr/libgcc.S:76: Error: too many memory references for
`add'
../../gcc/config/avr/libgcc.S:76: Error: no such instruction: `shift
multiplicand'
../../gcc/config/avr/libgcc.S:77: Error: no such instruction: `breq
__mulqi3_exit'
../../gcc/config/avr/libgcc.S:77: Error: no such instruction: `while
multiplicand!=0'
../../gcc/config/avr/libgcc.S:78: Error: no such instruction: `lsr
r24'
../../gcc/config/avr/libgcc.S:79: Error: no such instruction: `brne
__mulqi3_loop'
../../gcc/config/avr/libgcc.S:79: Error: no such instruction: `exit if
multiplier=0'
../../gcc/config/avr/libgcc.S:81: Error: too many memory references for
`mov'
../../gcc/config/avr/libgcc.S:81: Error: no such instruction: `result
to return register'
make[2]: *** [libgcc/./_mulqi3.o] Fehler 1
make[2]: Leaving directory
`/home/malte/unzip/gcc/gcc-3.3.4/obj-avr/gcc'
make: *** [all-gcc] Fehler 2

von Chris (Gast)


Lesenswert?

Hast du die binutils genau so installiert, wie in der Anleitung
beschrieben?

Bei mir kamen diese Fehler mal, als ich einfach "emerge binutils"
(gentoo) gemacht hab.

von Jörg Wunsch (Gast)


Lesenswert?

Yep, sieht sehr danach aus, als würde sich der avr-gcc die binutils
der Hostmaschine nehmen, also `as' statt `avr-as' usw.

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.