www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik ARM Cortex & GCC: Worauf achten für "optimalen" Code?


Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich bin nun von der 8051- in die ARM-Welt eingestiegen und verwende NxP 
Cortex-M0/M3 in Verbindung mit der LPCxpresso IDE von CodeRed und GCC.
IDE läuft, Debugging geht, soweit alles im Grünen.

Ich versuche nun im Selbststudium mit den Controllern fit zu werden, was 
auch klappt. Der UART läuft schon, der ADC wird grad zum Leben erweckt 
:)

Wo ich nun eher Anlaufschwierigkeiten habe ist der GCC, beispielsweise 
wie die Variablen bzgl. Speichermanagement gehandhabt werden, wie 
Parameterübergabe stattfindet, etc.
Ich komm leider momentan mit der in der LPCxpresso-IDE mitgelieferten 
GCC-Hilfe (noch) nicht zurecht, die Suchwörter spucken alles mögliche 
aus, nur aus meiner Sicht absolut nicht das was ich wissen will :(

Ein konkretes Beispiel: Ich hab die 8051er-Programme u.a. mit dem Keil 
C51 Compiler erstellt.
- Das Alignment spielte da keine Rolle, weil es ein 8-Bit-Controller 
ist. Wie ist das beim GCC & ARM? Wenn ich vier 8-Bit-Variablen (char) 
anlege, packt der GCC das automatisch in ein 32-Bit-Wort, oder verwendet 
er vier Mal ein 32-Bit-Wort? Wie kann ich das selber prüfen? Einfach mal 
ein paar Variablen anlegen und nach dem Build bei der Speicherangabe 
prüfen wieviel Bytes verbraten wurden? Ist das "aussagekräftig"?
Was ich bis jetzt in der Hilfe zum GCC rausgefunden habe, ist dass es 
beispielsweise beim Anlegen von structs nicht gepackt wird, d.h. im 
Speicher sind Lücken, wenn die struct nicht ein Vielfaches von 32-Bit 
verwendet. Das Packen kann durch ein Attribut bei der struct-Definition 
aktiviert werden.

- Der Keil C51-Compiler bzw. der Linker kann funktionsinterne Variablen 
überlagern, sodass Speicherplatz gespart wird. Allerdings geht das nur 
auf Sourcedatei-Ebene, d.h. die optimale Speicherauslastung hat man nur 
erreicht, wenn jede Funktion in einer eigenen Sourcedatei steht. Wie 
funktioniert dieser Mechanismus beim GCC?

- Beim Keil C51-Compiler wurden aufgrund der 8051-Architektur die 
Parameter in den Registern übergeben, auch hier war die Reihenfolge der 
Parameter bei der Übergabe wichtig, um optimalen Code zu erzeugen, da 
ansonsten zusätzlicher RAM für die Parameterübergabe verwendet wurde. 
Ich vermute beim GCC werden die Parameter über einen Stack übergeben, 
richtig? Wie lässt sich hier der Code optimieren? Wenn ich 
beispielsweise für eine Funktion vier Parameter habe, für die der Typ 
char ausreichend ist, werden dann vier 32-Bit-Wörter auf den Stack 
geschoben oder wird's gepackt? Bzw. wenn's nicht gepackt wird, wäre doch 
die Verwendung eines structs als Parameter geschickter, oder?

Ich versuche eben, meine Erfahrung mit dem 8051/Keil auf den ARM/GCC zu 
"portieren", um die Lernkurve bzgl. des Compilers zu optimieren :)
Wenn also jemand nützliche Links hat, bitte hier posten. Ich werde das 
gleiche tun, vielleicht ergibt sich ja eine ordentliche Linksammlung.

Ralf

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ralf schrieb:

> ist. Wie ist das beim GCC & ARM? Wenn ich vier 8-Bit-Variablen (char)
> anlege, packt der GCC das automatisch in ein 32-Bit-Wort, oder verwendet
> er vier Mal ein 32-Bit-Wort?

Register: jede einzeln in ein Register. Deklaration lokaler Variablen 
mit weniger als 32 Bits ist ineffizient.

Speicher: in 8 Bits gespeichert.

> Wie kann ich das selber prüfen?

Code ansehen, Adressen von Variablen im Dump suchen oder ggf.ausgeben.

> Was ich bis jetzt in der Hilfe zum GCC rausgefunden habe, ist dass es
> beispielsweise beim Anlegen von structs nicht gepackt wird, d.h. im
> Speicher sind Lücken,

Es wird nicht gepackt, es sei denn das wird ausdrücklich gewünscht. 
Gepackt ist recht ineffizient.

> - Der Keil C51-Compiler bzw. der Linker kann funktionsinterne Variablen
> überlagern, sodass Speicherplatz gespart wird.

51er speichern lokale Daten statisch im RAM, da sie mit einem Stack für 
Daten schlecht umgehen können. ARMs verwenden einen Stack und damit löst 
sich das Problem in Luft auf.

> Ich vermute beim GCC werden die Parameter über einen Stack übergeben,

Nein, Register. Nur wenns zu viele sind, was seltten ist.

Autor: nicht Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
>> ist. Wie ist das beim GCC & ARM? Wenn ich vier 8-Bit-Variablen (char)
>> anlege, packt der GCC das automatisch in ein 32-Bit-Wort, oder verwendet
>> er vier Mal ein 32-Bit-Wort?
>
> Register: jede einzeln in ein Register. Deklaration lokaler Variablen
> mit weniger als 32 Bits ist ineffizient.

das is so nicht richtig. Die Register sind immer 32 bit, es macht aber 
keinen unterschied, da man die Registeroperationen auch auf Bytes 
anwenden kann

Die erste Variable wir immer mit dword aligment angelegt.
struct {
 uint8_t a[3],
 uint32_t b[1]
} c;
in diesem Beispiel hat c eine größe von 8, weil eine Lücke zwischen a 
und b ist.

packed sollte man nur machen wenn man es wirklich braucht, z.B. bei 
Frames die über einen Bus kamen. Lieber beim erstellen selbst darauf 
auchten das keine Lücken entstehen.

Autor: Tec Nologic (tecnologic) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi zusammen,

Zum Attribut "packed" hätte ich ne Frage

struct{
uint8_t a;
uint8_t b;
uint8_t c;
uint8_t d;
} foo;

ist diese Struct ohne packed auch nur 4 Byte lang oder 16?
Also packt der GCC Variablen die in ein Dword passen zusammen rein und 
fängt erst beim Nächsten an, wenn er eine Var nicht mehr in das erste 
Dword bekommt?

MfG

Tec

Autor: nicht Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
4 byte. Hier muss die CPU sowieso einen byte weisen zugriff machen.

Autor: Tec Nologic (tecnologic) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke dachte ich brauche auch hier für packed.

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Prx & nicht Gast:
Danke für die Erklärungen.

>> Es wird nicht gepackt, es sei denn das wird ausdrücklich gewünscht.
>> Gepackt ist recht ineffizient.
> in diesem Beispiel hat c eine größe von 8, weil eine Lücke zwischen a
> und b ist.
Passt jetzt aber nicht zur der Antwort auf tecnologics Frage:
>> ist diese Struct ohne packed auch nur 4 Byte lang oder 16?
> 4 byte. Hier muss die CPU sowieso einen byte weisen zugriff machen.
Oder ist im Beispiel von nicht Gast die Lücke in der struct weil der 
uint32_t Member "geschickterweise" auf ein Word-Boundary gelegt wird?

Ralf

Autor: Tec Nologic (tecnologic) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
HI Ralf,

so verstehe ich das auch, der uin32_t passt nicht mehr in das Dword mit 
den 3 uint8_t deshalb beginnt der Compiler ein neues Dword, so erreicht 
er ja 32bit aligned Zugriff, und muss sich die Var nicht aus 2 Blöcken 
zusammen suchen. Darauf wollte ich ja auch hinnaus, weil ich mir da nie 
sicher war.
hab das dann immer in der Form gemacht.

struct{
uint32_t bFlag:1;
uint32_t reseve:31;
} foo __attribute_((_packed_));

so ist der Compiler gezwungen. ich werde das aber noch mal ohne Packed 
probieren.

MfG

Tec

Beitrag #2112137 wurde vom Autor gelöscht.
Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nicht Gast schrieb:

> das is so nicht richtig. Die Register sind immer 32 bit, es macht aber
> keinen unterschied, da man die Registeroperationen auch auf Bytes
> anwenden kann

Das ist so nicht richtig. Erstens kann man nur Lade/Speicheoperationen 
auf 8/16 Bits anwenden, nicht aber ALU Operationen. Zweitens stehen 
(deshalb) bestimmte Compiler-Konventionen im Weg.
short reg16(short a, short b)
{
    short x = a + b;
    return x;
}
int reg32(int a, int b)
{
    int x = a + b;
    return x;
}
wird zu
reg16:  add  r0, r1, r0
        mov  r0, r0, asl #16
        mov  r0, r0, asr #16
        bx   lr
reg32:  add  r0, r1, r0
        bx   lr

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> 51er speichern lokale Daten statisch im RAM, da sie mit einem Stack für
> Daten schlecht umgehen können.

Nö, der 8051 kann auch mit dem Stack arbeiten.
Allerdings ist es erheblich effizienter, direkt auf den SRAM 
zuzugreifen. Es muß kein Stackframe angelegt werden und viele Befehle 
können im SRAM gemacht werden (MOV, INC, DEC, DJNZ, ANL, XRL, ..).
Außerdem wird selten ein RTOS verwendet, daher lassen sich lokale 
Variablen überlagern.

A. K. schrieb:
> ARMs verwenden einen Stack und damit löst
> sich das Problem in Luft auf.

Sie können nichts direkt im SRAM ausführen (RISC), daher ist es fast 
egal, ob aus dem SRAM laden oder PUSH/POP.


Peter

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

> Nö, der 8051 kann auch mit dem Stack arbeiten.
> Allerdings ist es erheblich effizienter, direkt auf den SRAM
> zuzugreifen.

Ich habe nichts anderes behauptet, mit schlecht=ineffizient.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:

>> ARMs verwenden einen Stack und damit löst
>> sich das Problem in Luft auf.
>
> Sie können nichts direkt im SRAM ausführen (RISC), daher ist es fast
> egal, ob aus dem SRAM laden oder PUSH/POP.

Es ging hier um die Platzfrage.

Wenn lokale Variablen statisch angelegt werden, dann ist es aus 
Platzgründen sinnvoll, dass der Compiler etwas Aufwand treibt, um solche 
Variablen von sich nicht gegenseitig aufrufenden Funktionen überlagern 
zu können.

Wenn lokale Variablen in Registern bzw. auf einem Stack angelegt werden, 
dann löst sich diese Platzfrage ohne jede Optimierung ganz von allein, 
da diese Überlagerung eine implizite Eigenschaft des Stacks ist.

Autor: Stellaris (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier eine App Note von TI zu den Cortex M3 von TI:

Optimizing Code Performance and Size for Stellaris® Microcontrollers

Das dürfte auch für deine NXP gelten.

Gruss

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.