Forum: Mikrocontroller und Digitale Elektronik RAM doppelt belegt bei lokalen Variablen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Leon0402 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich programmiere schon seit längerem an einem größerem Programm für den 
atmega328p in C++. Letzeres einfach, weil ich das größere Projekt 
objektorientiert umsetzen wollte und templates etc. nutzen wollte ... 
war auch quasi ein Art test wie gut das mit C++ auf einem 8bit 
Microcontroller funtkioniert. (Eig. sehr gut muss ich sagen, abgesehen 
von fehlender OOP Integration für Interrupts).

Nun kam ich plötzlich gestern an den Punkt an, dass meine Serial Library 
nicht mehr funktionierte, was ich als RAM Problem ausfindig gemacht 
habe. Da ich da wenig Ahnung habe, habe ich ein paar Experimente gemacht 
mit folgendem Code

#include "Stream.h"

int freeRam();
void useRam();

//2015 Bytes RAM, wenn useRam() Methode auskommentiert ist
//1535 Bytes RAM mit useRam() Methode, aber nicht aufgerufen
//1535 Bytes RAM mit useRam() Methode und 1032 Bytes RAM während dem 
Aufruf
int main() {
  useRam();
  avr::cout << freeRam() << '\n';
  while(1);
}

int freeRam () {
   extern int __heap_start, *__brkval;
   int v;
   return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) 
__brkval);
}

void useRam() {
  float points[][3] = {{15, 0, 0},    {7.5, 0, 0},  {15, 0, 0} ,  {0, 0, 
0},
                       {0, -15, 0},   {0, -7.5, 0}, {0, -15, 0},  {0, 0, 
0},
                       {0, 0, 15},    {0, 0, 7.5},  {0, 0, 15},   {0, 0, 
0},
                       {0, 15, 15},   {0, 0, 0},    {0, -15, 15}, {0, 0, 
0},
                       {0, -15, -15}, {0, 0, 0},    {0, 15, -15}, {0, 0, 
0},
                       {-15, 0, 0},   {-7.5, 0, 0}, {-15, 0, 0},  {0, 0, 
0},
                       {0, 15, 0},    {0, 7.5, 0},  {0, 15, 0},   {0, 0, 
0},
                       {0, 0, -15},   {0, 0, -7.5}, {0, 0, -15},  {0, 0, 
0},
                       {0, -15, -15}, {0, 0, 0},    {0, 15, -15}, {0, 0, 
0},
                       {0, 15, 15},   {0, 0, 0},    {0, -15, 15}, {0, 0, 
0}};
  for(float (&point)[3]: points) {
    for(float& value: point ) {
      avr::cout << value << '\n';
    }
  }
  avr::cout << freeRam() << '\n';
}

Dabei ist mir folgendes aufgefallen. Habe ich die Methode mit dem 
übergroßen array auskommentiert, habe ich 2015 Bytes zur Verfügung. Habe 
ich die Methode aber rufe sie nicht auf, habe ich nur noch 1500 Bytes 
zur Verfügung an RAM.Rufe ich sie auf, schrumpft der RAM während des 
Ausrufs nochmal auf 1000 Bytes.
Deklariere ich das Array global statt lokal habe ich auch 1500 Bytes zur 
Verfügung, bei Aufruf der Methode etwas weniger.

Letzeres kann ich sehr gut nachvollziehen. 120 float * 4 Bytes ... knapp 
500 Bytes die dauerhaft belegt werden, da es global ist. Bei Aufruf der 
Methode wird für etwas RAM verbraucht, um sich zu merken woher man kam 
(soweit ich weiß). Auch bei der lokalen variante macht es Sinn, dass RAM 
in Höhe von 500 Bytes belegt wird während dem Aufruf und anschließend 
wieder freigegeben wird.

Was aber keinen Sinn für mich ergibt ist, dass das lokale Array 
scheinbar wie das globale Array immer 500 Bytes belegt (selbst wenn die 
Methode nicht aufgerufen wird). Ruft man die Methode auf, werden nochmal 
während des Aufrufs 500 Bytes belegt.

Ich habe gelesen, dass das irgendwie Architektur bedingt ist, konnte den 
Erklärungen aber nicht so ganz folgen. Es wäre sehr nett, wenn mir das 
jemand nochmal in verständlich erläutert.

Außerdem die Frage, sollte dieses Verhalten korrekt sein, warum sollte 
ich dann lokale Variablen verwenden, wenn sie im Gegensatz zu globalen 
Variablen scheinbar doppelt so viel RAM belegen? (Neben dem kleinerem 
scope ist doch eig der Vorteil an lokalen Variablen, dass sie nur 
während dem Aufruf RAM belegen).

Vielen Dank!

Grüße
Leon

von Stefan ⛄ F. (stefanus)


Bewertung
-1 lesenswert
nicht lesenswert
Ich vermute, dass das Verhalten sehr stark von den Compiler- und Linker- 
Optionen abhängt, welche die Optimierung steuern.

von A. S. (achs)


Bewertung
0 lesenswert
nicht lesenswert
Schau nach, wo die Initialisierungs-Werte des Arrays liegen. Vielleicht 
im RAM, weil flash-lesen nach Startup zu teuer ist.

Es wird ja bei jedem Aufruf neu übergebügelt.

von Leon0402 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die schnellen Antworten!

Ich habe den Compiler auf die Optimierungstufe Os für Speicher 
Optimierung eingestellt. Sollte es da andere Optimierungen geben, die 
einen doppelten RAM Bedarf vermeiden, wäre es sehr nett, wenn du oder 
andere sie mir mitteilen.
Da ich Probleme mit einem zu kleinem RAM habe, liegt es natürich in 
meinem Interesse das zu optimieren (Für so große Arrays würde ich da den 
PROGMEN nutzen ... das Beispiel diente nur zum testen und 
verdeutlichen).

@achim S. deine Antwort verstehe ich nicht so ganz. Eig möchte ich ja, 
sofern ich die Methode nicht aufrufe, dass das array gar nicht 
initalisiert wird ... wie man es doch eig. von lokalen Variablen 
erwartet? Dies scheint nicht der Fall zu sein ... Ich wüsste auch nicht 
wie ich das nachschauen kann?

von Stefan ⛄ F. (stefanus)


Bewertung
-1 lesenswert
nicht lesenswert
Leon0402 schrieb:
> Eig möchte ich ja,
> sofern ich die Methode nicht aufrufe, dass das array gar nicht
> initalisiert wird

Warum hast du dann Initialisierungswerte hin geschrieben?

Wenn du das Schlüsselwort "static" verwendet, gibt es im Speicher nur 
eine Instanz des Arrays und dann wird es auch nur einmal beim 
Programmstart (vor Ausführung der main() Funktion) initialisiert.

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]
  • [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.