mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Arduino Compiler Speicherverwaltung


Autor: Lothar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann mir mal jemand die Arduino Compiler Speicherverwaltung erklären?

Ich mache einen leeren Sketch mit folgendem:

void loop()
{
  // volatile unsigned char var1 = 1;
  // volatile unsigned char var2[] = {1, 1};
  // volatile unsigned char var3[] = {1, 1, 1};
}

Ergebnis: "Global variables use 9 bytes"

Das kann ja sein dann gibt es eben einige "versteckte" globale Variable. 
Dann mache ich folgendes:

void loop()
{
  volatile unsigned char var1 = 1;
  volatile unsigned char var2[] = {1, 1};
  // volatile unsigned char var3[] = {1, 1, 1};
}

Ergebnis: "Global variables use 9 bytes"

Auch das ist nachvollziehbar das sind lokale Variablen die beim Aufruf 
auf dem Stack angelegt werden. Nun mache ich noch folgendes:

void loop()
{
  // volatile unsigned char var1 = 1;
  // volatile unsigned char var2[] = {1, 1};
  volatile unsigned char var3[] = {1, 1, 1};
}

Ergebnis "Global variables use 13 bytes"

Wieso das? Reserviert der Compiler für lokale Arrays > 3 Byte globalen 
Speicher? Ist das eine Art Sicherung gegen Stacküberlauf?

Autor: foobar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bis zu einer bestimmten Größe werden die Arrays "von Hand", also mit 
einzelnen Instruktionen initialisiert und darüber hinaus mit einer 
Kopierschleife. Das ist Teil des Codegenerators/Optimierers.

Autor: foobar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Btw, volatile auto vars sind schon ziemlich merkwürdig - da gehört 
zumindest nen Kommentar hinter ;-)

Autor: foobar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch zur Erläuterung: lokale Variablen befinden sich auf dem Stack. Wenn 
sie initialisiert werden sollen, muß das für jeden einzelnen Aufruf vom 
Code händisch gemacht werden.

Bei wenigen Elementen macht der Compiler
   // aus
   char var[2] = { 1, 2};
   // dies
   char var[2]; var[0]=1; var[1]=2;

Werden es mehr Elemente (z.B. 5), macht er daraus
    static char init937[5] = { 1,2,3,4,5}; // anonymes statisches Array
    char var[5]; memcpy(var, init937, sizeof(init937));

Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und wenn das ganze auf einem AVR läuft, muss für ein normales memcpy 
die Quelle erst mal aus dem Flash ins RAM kopiert werden - das könnte 
die Ursache für den Speicherverbrauch sein; statt memcpy sollte hier 
eigentlich so etwas wie memcpy_P verwendet werden.

Autor: Axel S. (a-za-z0-9)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rufus Τ. F. schrieb:
> Und wenn das ganze auf einem AVR läuft, muss für ein normales /memcpy/
> die Quelle erst mal aus dem Flash ins RAM kopiert werden - das könnte
> die Ursache für den Speicherverbrauch sein; statt memcpy sollte hier
> eigentlich so etwas wie memcpy_P verwendet werden.

Ich bin recht zuversichtlich, daß der Compiler das so organisiert. Wenn 
er es genauer wissen will, muß der TE halt mal ins map-file schauen.

Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Axel S. schrieb:
> Ich bin recht zuversichtlich, daß der Compiler das so organisiert.

Täte er das, wäre der beschriebene Effekt aber nicht feststellbar.

Autor: Axel S. (a-za-z0-9)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rufus Τ. F. schrieb:
> Axel S. schrieb:
>> Ich bin recht zuversichtlich, daß der Compiler das so organisiert.
>
> Täte er das, wäre der beschriebene Effekt aber nicht feststellbar.

Wir wissen gar nicht, was die Ursache für das beschriebene Verhalten 
ist. Wir wissen noch nicht mal, ob der Platz in .data oder .bss 
verbraucht wird. Man könnte das natürlich herausfinden, wenn man 
wöllte. Aber ich bin erstens faul, zweitens beschäftigt und finde es 
drittens auch vollkommen unwichtig.

Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Axel S. schrieb:
> Aber ich bin erstens faul, zweitens beschäftigt und finde es drittens
> auch vollkommen unwichtig.

Dann sind wir uns ja einig ...

Autor: Lothar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar schrieb:
> Ist das eine Art Sicherung gegen Stacküberlauf?

Ich habe für eine PC-Software eine Funktion zur Datenkomprimierung 
gemacht. Die hat grosse lokale Arrays. Die will ich nun auch auf uC 
nutzen. Zuerst auf 8051: hier ist natürlich klar - die grossen lokalen 
Arrays müssen global gemacht werden da sonst Stacküberlauf. Auf Arduino 
musste ich erstaunt feststellen dass es scheinbar keinen Unterschied 
macht ob die grossen Arrays lokal bleiben oder global gemacht werden. 
Wenn man sich darauf verlassen kann müssten PC Funktionen nicht mehr 
angepasst werden. Wenn nicht würde ich sicherheitshalber auch auf 
Arduino immer alle grossen Arrays global machen. Dann bekomme ich ja vom 
Compiler angezeigt wenn der Speicher voll ist.

foobar schrieb:
> volatile auto vars sind schon ziemlich merkwürdig

Habe ich nur in den Beispielen so gemacht da nicht verwendete Variablen 
sonst wegoptimiert werden.

Autor: Axel S. (a-za-z0-9)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar schrieb:
>
> Ich habe für eine PC-Software eine Funktion zur Datenkomprimierung
> gemacht. Die hat grosse lokale Arrays. Die will ich nun auch auf uC
> nutzen. Zuerst auf 8051: hier ist natürlich klar - die grossen lokalen
> Arrays müssen global gemacht werden da sonst Stacküberlauf. Auf Arduino
> musste ich erstaunt feststellen dass es scheinbar keinen Unterschied
> macht ob die grossen Arrays lokal bleiben oder global gemacht werden.

Diese Aussage ist mit Vorsicht zu genießen. "Arduino" ist keine 
einheitliche Hardware-Plattform. Offiziell unterstützt werden AVR und 
Cortex-M Kerne, inoffiziell gibts das Framework für noch mehr. Abhängig 
davon, was da an Hardware auf deinem Arduino ist, hat der Stack entweder 
eine begrenzte Größe oder er kann den ganzen RAM nutzen.

"Große lokale Arrays" klingt aber sowieso falsch. Wenn du die Funktionen 
nicht rekursiv nutzen willst, mach sie wenigstens static. Dann müssen 
sie nicht bei jedem Aufruf der Funktion initialisiert werden.


> ... würde ich sicherheitshalber auch auf
> Arduino immer alle grossen Arrays global machen. Dann bekomme ich
> ja vom Compiler angezeigt wenn der Speicher voll ist.

Zwar nicht vom Compiler, aber ja. Das ist durchaus ein Vorteil.

Autor: Lothar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Axel S. schrieb:
> "Arduino" ist keine einheitliche Hardware-Plattform

Das Board hat einen ATmega2560

Autor: Stefanus F. (stefanus)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe mal ein Assembler Listing aus der *.elf Datei erstellt, die der 
Compiler (mit dem 3-Byte Array) erzeugt hat:

avr-objdump -h -S /tmp/arduino_build_482746/test.ino.elf > test.txt

Die Ausgabe von
avr-size /tmp/arduino_build_482746/test.ino.elf
ist:
   text     data      bss      dec      hex  filename
    712        4        9      725      2d5  /tmp/arduino_build_482746/test.ino.elf

Offensichtlich addiert die IDE die Größen vom data und bss Segment 
(siehe https://www.nongnu.org/avr-libc/user-manual/mem_sections.html).

Wenn ich ein komplett leeres Programm compiliere, ist auch bei mir die 
Ausgabe:
   text     data      bss      dec      hex  filename
    656        0        9      665      299  /tmp/arduino_build_482746/test.ino.elf

Ebenso, wenn ich die beiden kürzeren Arrays einkommentiere.

Ich bin jetzt auch ratlos.

: Bearbeitet durch User
Autor: Axel S. (a-za-z0-9)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Ich habe mal ein Assembler Listing aus der *.elf Datei erstellt, die der
> Compiler (mit dem 3-Byte Array) erzeugt hat

Ein Mapfile wäre hilfreicher gewesen.
LDFLAGS = -Wl,-Map=$(OBJDIR)/$(TARGET).map,--cref

> Offensichtlich addiert die IDE die Größen vom data und bss Segment

Ja. Und noch noinit.
AVR Memory Usage
----------------
...
Data:        xxx bytes (yy.y% Full)
(.data + .bss + .noinit)


> Wenn ich ein komplett leeres Programm compiliere
>
>    text     data      bss      dec      hex  filename
>     656        0        9      665      299 
> 
>
> Ebenso, wenn ich die beiden kürzeren Arrays einkommentiere.
> Ich bin jetzt auch ratlos.

Na ja. Die 9 Bytes bss sind anscheinend nichtininitialisierte 
globale/statische Variablen aus dem Arduino Core. Im Zweifel einfach 
direkt mit dem avr-gcc aus einem .c compilieren. Der Arduino-Krempel 
macht am Ende auch nichts anderes. OK, er compiliert es als C++. Könnte 
auch einen Unterschied ausmachen.

Autor: foobar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich bin jetzt auch ratlos.

Wieso? Was ist denn noch unklar?  Die 9 Byte im .bss sind timer0_millis, 
timer0_fract und timer0_overflow_count. Und die Initialisierungsdaten 
der auto-Variablen landen im .data.

Hier der Code, den GCC erzeugt (meiner macht erst ab 5 Elementen die 
Kopierschleife; Listing ohne Prolog etc):
// void foo() { volatile char var[] = { 1, 2 }; }
foo:
  ...
  ldi r24,lo8(1)
  ldi r25,lo8(2)
  std Y+2,r25
  std Y+1,r24
  ...
  ret

// void bar() { volatile char var[] = { 1, 2, 3, 4, 5 }; }
  .section  .rodata
.LC0:
  .byte  1
  .byte  2
  .byte  3
  .byte  4
  .byte  5
  .text
bar:
  ...
  ldi r24,lo8(5)
  ldi r30,lo8(.LC0)
  ldi r31,hi8(.LC0)
  movw r26,r28
  adiw r26,1
  0:
  ld r0,Z+
  st X+,r0
  dec r24
  brne 0b
  ...
  ret

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dass das Arduino Framework 9 Bytes belegt, überrascht mich nicht.

Aber das was darüber hinaus geht:
volatile unsigned char var1 = 1;            ---> 0 Bytes

volatile unsigned char var2[] = {1, 1};     ---> 0 Bytes

volatile unsigned char var3[] = {1, 1, 1};  ---> 4 Bytes

Wie kann das sein?

: Bearbeitet durch User
Autor: foobar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist einfach ein Aspekt des Codegenerators oder des Optimizers. Wie 
er genau dazu kommt, ist doch Peng.

Ein Entscheidungsgang könnte bei meinem geposteten Kode z.B. sein: er 
braucht 2 Instruktionen pro Arrayelement wenn er jedes Element einzeln 
initialisiert; für die Schleife braucht er 9 Instruktionen, egal wie 
viele Elemente es sind. Also ist es ab 5 Elementen günstiger, die 
Schleife zu benutzen. Ob das jetzt wirklich das Kriterium ist (besonders 
gut ist es ja nicht), weiß ich nicht - dazu müsste man in den GCC 
reinschauen und wird wohl deutlich komplexer sein.

Autor: Peter D. (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar schrieb:
> Auf Arduino
> musste ich erstaunt feststellen dass es scheinbar keinen Unterschied
> macht ob die grossen Arrays lokal bleiben oder global gemacht werden.

Der 8051 hat verschiedene RAM-Bereiche (data, xdata), der AVR nicht. Man 
kann auch beim 8051 große Daten lokal ablegen, das ist dann das LARGE 
Speichermodell oder sie als xdata deklarieren.

Autor: Lothar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist noch eine andere Baustelle. Da der Arduino C++ Compiler 
scheinbar das Codewort flash nicht unterstützt müssten alle 8051 code 
const Array Zugriffe auf pgm_read_byte geändert werden. Da das Aufwand 
ist mache ich die meist auch global - bis das RAM voll ist.

Autor: Arduino Fanboy D. (ufuf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar schrieb:
> Da der Arduino C++ Compiler
> scheinbar das Codewort flash nicht unterstützt

Das ist so richtig und auch falsch.

Es gibt keinen Arduino C++ Compiler.
Hat also nichts mit Arduino an sich zu tun.

Es ist wird z.B. der AVR-Gcc genutzt.
Und dort gibt es flash nur für C, nicht in C++


Andere Compiler, für andere µC, mögen das unterstützen.... KA

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.

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