Forum: Mikrocontroller und Digitale Elektronik Atmega objdump: Speicherfresser finden


von chris (Gast)


Lesenswert?

Wie finde ich mit avr-objdump die großen Speicherfresser?

Problem:

>Globale Variablen verwenden 1537 Bytes (75%) des dynamischen Speichers, 511 Bytes 
für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

avr-objdump -t meinProgramm.ino.elf > map.txt

Wenn ich's richtig weiß, ist das RAM im .bss segment.
Muss man alles von Hand zusammenzählen?

von foobar (Gast)


Lesenswert?

Im RAM landen beim AVR .bss und .data.  Das solltest du auch nicht ganz 
voll machen, da noch etwas für den Stack benötigt wird.

In der vorletzten Spalte der objdump-Ausgabe ist doch die Größe des 
Objekts - da sieht man eigentlich schnell, wer viel braucht.

von g457 (Gast)


Lesenswert?

> Wie finde ich mit avr-objdump die großen Speicherfresser?

Einfacher isses in den Code zu schauen, i.d.R. suchst Du nämlich nicht 
"die Großen" sondern vielviel Kleine. Zumindest bei atmegas dieser 
Größe.

von chris (Gast)


Lesenswert?

>Im RAM landen beim AVR .bss und .data.  Das solltest du auch nicht ganz
>voll machen, da noch etwas für den Stack benötigt wird.

Das dachte ich mir schon. Bei ca. 200Byte Reserve fängt das Programm an, 
sich beim Start aufzuhängen.

>In der vorletzten Spalte der objdump-Ausgabe ist doch die Größe des
>Objekts - da sieht man eigentlich schnell, wer viel braucht.

Das habe ich schon gesehen. Ich wollte es nur nicht manuell durchsuchen 
müssen.
Was ist der Unterschied zwischen .bss und .data ?

von chris (Gast)


Lesenswert?

g457 (Gast)
>Einfacher isses in den Code zu schauen, i.d.R. suchst Du nämlich nicht
>"die Großen" sondern vielviel Kleine. Zumindest bei atmegas dieser
>Größe.

Ja, es sind scheinbar viele kleine. Was mich ein wenig wundert ist, dass 
Objekte die noch nicht instantiiert sind und in einer Liste später 
gesammelt werden, schon ca. 10 Byte .data zu verbrauchen scheinen.

von Oliver S. (oliverso)


Lesenswert?

Bei über 1500 Byte an globalen Variablen sucht man nach großen Arrays. 
Diese Menge bekommt man mit vielen kleinen Variablen nicht mal annähernd 
zusammen.

Oliver

von Dyson (Gast)


Lesenswert?

chris schrieb:
> Ich wollte es nur nicht manuell durchsuchen
> müssen.

Wie denn sonst? Du hast den Mist doch auch selbst da reingeschrieben.

von Rolf M. (rmagnus)


Lesenswert?

chris schrieb:
> Was mich ein wenig wundert ist, dass Objekte die noch nicht instantiiert
> sind und in einer Liste später gesammelt werden, schon ca. 10 Byte .data
> zu verbrauchen scheinen.

Was meinst du mit "noch nicht instantiiert"? Globale Variablen sind 
immer da und brauchen immer Speicher. Es gibt kein "noch nicht 
instantiiert".

: Bearbeitet durch User
von Dyson (Gast)


Lesenswert?

Rolf M. schrieb:
> Was meinst du mit "noch nicht instantiiert"?

Der hat keine Ahnung und will hier auf wichtig machen.

von Hmmm (Gast)


Lesenswert?

chris schrieb:
> Was ist der Unterschied zwischen .bss und .data ?

.data sind initialisierte Variablen, also z.B. String Literals wie in 
printf("Test"); - sofern Du nicht dafür Sorge trägst, dass sie erst bei 
Bedarf vom Flash ins RAM kopiert werden.

.bss sind nicht initialisierte Variablen, also z.B. globale Variablen 
ohne zugewiesenen Wert.

von chris (Gast)


Lesenswert?

von Hmmm (Gast)
>chris schrieb:
>> Was ist der Unterschied zwischen .bss und .data ?

>.data sind initialisierte Variablen, also z.B. String Literals wie in
>printf("Test"); - sofern Du nicht dafür Sorge trägst, dass sie erst bei
>Bedarf vom Flash ins RAM kopiert werden.

>.bss sind nicht initialisierte Variablen, also z.B. globale Variablen
>ohne zugewiesenen Wert.

Danke dafür.

Hier mal der Speicherverbrauch der Basisklasse "Component":

008003ba g     O .data  00000010 .hidden _ZTV9Component

Tatsächlich werden auch für jede abgeleitete Klasse 16 Bytes .data 
reserviert, obwohl kein einziges Objekt beim Programmstart instantiiert 
ist. Die Objekte werden erst während des Programmlaufs initialisiert. Es 
können 0 bis mehrere Objekte einer Klasse durch das Programm erzeugt 
werden.

Hier die Zeile des Compiler-Laufs, um das Setup zu sehen:

"/home/christoph/tools/arduino-1.8.5/hardware/tools/avr/bin/avr-g++" -c 
-g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections 
-fdata-sections -fno-threadsafe-statics -MMD -flto -mmcu=atmega328p 
-DF_CPU=16000000L -DARDUINO=10805 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR 
"-I/home/christoph/tools/arduino-1.8.5/hardware/arduino/avr/cores/arduin 
o" 
"-I/home/christoph/tools/arduino-1.8.5/hardware/arduino/avr/variants/eig 
htanaloginputs" 
"-I/home/christoph/tools/arduino-1.8.5/hardware/arduino/avr/libraries/EE 
PROM/src"  "-I/home/christoph/Arduino/libraries/MemoryFree" 
"/tmp/arduino_build_683962/sketch/Component.cpp" -o 
"/tmp/arduino_build_683962/sketch/Component.cpp.o"

Warum verbrauchen die Klassen 16Bytes an initialiertem .data-RAM, ohne 
ein einziges Objekt?

von Wilhelm M. (wimalopaan)


Lesenswert?

chris schrieb:
> Warum verbrauchen die Klassen 16Bytes an initialiertem .data-RAM, ohne
> ein einziges Objekt?

Das liegt bei AVR an der vtable, die der gcc blöderweise ins RAM packt. 
Jede virtuelle Elementfunktion benötigt darin dann 2 Byte zzgl. der bei 
Null-Einträge am Anfang sowie dem virtuellen dtor.

von chris (Gast)


Lesenswert?

>Das liegt bei AVR an der vtable,

Danke dafür, so was habe ich schon vermutet.
Am Anfang des Projektes habe ich noch überlegt, ob ich es in C machen 
soll und die Klassen von Hand via Strukturen basteln. Es scheint mir, 
das wäre der bessere Weg gewesen.
Ich habe 30 Klassen a 16Bytes was mich ca. ein viertel des Speichers 
kostet.

von Wilhelm M. (wimalopaan)


Lesenswert?

chris schrieb:
> Es scheint mir,
> das wäre der bessere Weg gewesen.

Die Frage ist, ob Du überhaupt Laufzeitpolymorphie benötigst. Meistens 
ist das nämlich gar nicht der Fall und man kann das durch statische 
Polymorphie ersetzen.

von Oliver S. (oliverso)


Lesenswert?

chris schrieb:
> Ich habe 30 Klassen a 16Bytes was mich ca. ein viertel des Speichers
> kostet.

Was allerdings bedeutet, daß nichtmal eine Instanz von jeder Klasse ins 
RAM passt. Das Konzept erscheint nicht ganz durchdacht.

Oliver

Beitrag #6164574 wurde von einem Moderator gelöscht.
von Vincent H. (vinci)


Lesenswert?

Folgendes Tool könnte vielleicht nützlich sein:
http://www.sikorskiy.net/prj/amap/

"amap : A tool to analyze .MAP files produced by 32/64-bit Visual Studio 
compiler and report the amount of memory being used by data and code.
This app can also read and analyze MAP files produced by the GCC, 
Xbox360, Wii, PS3 (gcc and SNC), and PS4 compilers."

Das erleichtert zumindest das Suchen etwas...

von Andreas B. (bitverdreher)


Lesenswert?

avr-size ist Dein Freund.

von Wilhelm M. (wimalopaan)


Lesenswert?

Andreas B. schrieb:
> avr-size ist Dein Freund.

Besser:
1
avr-nm -CS --size-sort test.elf

von Pandur S. (jetztnicht)


Lesenswert?

Aaaaahhhhhh ... Objekte auf einem AVR ... aaaaahhh.
Man kann auchb wirklich alles falsch machen.

Was sollen die denn bringen ? Eine linked list ?
Sollte man immer alles statisch allozieren.

von Wilhelm M. (wimalopaan)


Lesenswert?

Joggel E. schrieb:
> Aaaaahhhhhh ... Objekte auf einem AVR ... aaaaahhh.
> Man kann auchb wirklich alles falsch machen.

Was hast Du gegen Objekte?

Du meinst virtuelle Elementfunktionen und Laufzeitpolymorphie.

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Hi

https://github.com/ARMmbed/mbed-os-linker-report

Ganz toll um sich einen Überblick zu verschaffen.

Matthias

von Isolanporzellator (Gast)


Lesenswert?

|
       |
       V
--> avr-g++ <--
       ^
       |
       |

Har har har!

Recht  so!

von Oliver S. (oliverso)


Lesenswert?

chris schrieb:
> Wie finde ich mit avr-objdump die großen Speicherfresser?

Ist das Projekt geheim, oder kannst du das hier mal hochladen?

Oliver

von Pandur S. (jetztnicht)


Lesenswert?

>> Joggel E. schrieb:
>> Aaaaahhhhhh ... Objekte auf einem AVR ... aaaaahhh.
>> Man kann auchb wirklich alles falsch machen.

>Was hast Du gegen Objekte?
>
>Du meinst virtuelle Elementfunktionen und Laufzeitpolymorphie.


Ja, ich habe etwas gegen Objekte auf Controllern.

Was sollen die bringen ?

von vn nn (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Joggel E. schrieb:
>> Aaaaahhhhhh ... Objekte auf einem AVR ... aaaaahhh.
>> Man kann auchb wirklich alles falsch machen.
>
> Was hast Du gegen Objekte?

Vermutlich hatte er gerade einen Schlaganfall.

Joggel E. schrieb:
> Ja, ich habe etwas gegen Objekte auf Controllern.
>
> Was sollen die bringen ?

Sauberen Code? Gegenfrage, wo siehst du das Problem darin, wenn man sie 
richtig verwendet?

von Wilhelm M. (wimalopaan)


Lesenswert?

Joggel E. schrieb:
> Ja, ich habe etwas gegen Objekte auf Controllern.
1
int x{};
2
3
A y{};

Hier hast Du ZWEI Objekte. Und jetzt erkläre, was daran verwerflich sein 
soll.

von Walter T. (nicolas)


Lesenswert?

Vincent H. schrieb:
> Folgendes Tool könnte vielleicht nützlich sein:
> http://www.sikorskiy.net/prj/amap/

Stimmt. Gerade ausprobiert und jetzt in meine Werkzeugsammung 
aufgenommen. Danke für den Tipp!

von Sebastian S. (amateur)


Lesenswert?

Textest Du die Leute voll?
Bei Textvariablen wird diese in's RAM kopiert, was mal schnell ein paar 
Bytes verbraucht.
Also: Texte explizit im Flash ablegen.

Ansonsten fällt mir nur noch ein:
So viele Variablen, wie möglich, lokal, in den Funktionen anlegen. Da 
brauchst Du kein malloc und so'n Kram und das Recycling passiert beim 
Rücksprung.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Sebastian S. schrieb:
> Ansonsten fällt mir nur noch ein:
> So viele Variablen, wie möglich, lokal, in den Funktionen anlegen. Da
> brauchst Du kein malloc und so'n Kram und das Recycling passiert beim
> Rücksprung.

Es hat aber auch einen großen Nachteil: Es ist zur Compilezeit nicht 
überschaubar, wie viel RAM man tatsächlich braucht. Und wenn die Daten 
zu groß für den Speicher sind, gibt's einfach nur fehlerhaftes 
Verhalten.

von Sebastian S. (amateur)


Lesenswert?

@Rolf M.

>Es hat aber auch einen großen Nachteil: Es ist zur Compilezeit nicht
>überschaubar, wie viel RAM man tatsächlich braucht. Und wenn die Daten
>zu groß für den Speicher sind, gibt's einfach nur fehlerhaftes
>Verhalten.
Stimmt, aber nur so kommt man auf ein "Minimum" zur Laufzeit und kann 
nicht vergessen zu "putzen".

von vn nn (Gast)


Lesenswert?

Sebastian S. schrieb:
>>Es hat aber auch einen großen Nachteil: Es ist zur Compilezeit nicht
>>überschaubar, wie viel RAM man tatsächlich braucht. Und wenn die Daten
>>zu groß für den Speicher sind, gibt's einfach nur fehlerhaftes
>>Verhalten.
> Stimmt, aber nur so kommt man auf ein "Minimum" zur Laufzeit und kann
> nicht vergessen zu "putzen".

Vor allem, da der Compiler dann versuchen wird, so viel wie möglich ohne 
RAM-Nutzung in den Registern zu erledigen.

von Compiler (Gast)


Lesenswert?

> Vor allem, ...

Compiler denken nicht mit. Es gibt auch keine Optimierung die
"so viel wie möglich ohne RAM-Nutzung in den Registern zu erledigen."

Das waere allenfalls ueber eine Optimierung in Richtung minimaler
Laufzeit des Programms steuerbar.

Im Stack gehaltene Variable sind am Funktionsende auch nicht
mehr zugreifbar.

Und ob ich die Variablen statisch deklariere oder in der main
vom Stack hole, macht keinen Unterschied.

von Rolf M. (rmagnus)


Lesenswert?

Compiler schrieb:
>> Vor allem, ...
>
> Compiler denken nicht mit. Es gibt auch keine Optimierung die
> "so viel wie möglich ohne RAM-Nutzung in den Registern zu erledigen."

Doch, natürlich gibt es die. Das dürfte sogar zu den ältesten 
Optimierungen überhaupt zählen.

> Und ob ich die Variablen statisch deklariere oder in der main
> vom Stack hole, macht keinen Unterschied.

Es macht einen Unterschied. Darum ging es ja gerade. Bei statischen 
Variablen sehe ich bereits zur Linkzeit, wie viel Platz sie brauchen. 
Dafür brauchen sie den Platz über die gesamte Laufzeit des Programms. 
Automatische Variablen dagegen existieren nur während der Laufzeit der 
Funktion, in der sie definiert sind. Davor und danach existieren sie 
nicht. Dafür sieht man nicht, wieviel am Ende wirklich an Platz 
gebraucht wird. Allerdings werden sie wenn möglich rein in Registern 
gehalten, wodurch sie zum Teil gar keinen Speicher brauchen.

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

vn nn schrieb:
> Sebastian S. schrieb:
>>>Es hat aber auch einen großen Nachteil: Es ist zur Compilezeit nicht
>>>überschaubar, wie viel RAM man tatsächlich braucht. Und wenn die Daten
>>>zu groß für den Speicher sind, gibt's einfach nur fehlerhaftes
>>>Verhalten.
>> Stimmt, aber nur so kommt man auf ein "Minimum" zur Laufzeit und kann
>> nicht vergessen zu "putzen".
>
> Vor allem, da der Compiler dann versuchen wird, so viel wie möglich ohne
> RAM-Nutzung in den Registern zu erledigen.

Das macht der eh, wenn man nicht gerade Code hat, der nur mit -O0 
richtig läuft. Was der AVR auch macht: Globale Variablen immer mit 
kompletter Adresse ansprechen (also 2-Wort-LDS-STS, statt via 
Frameptr+ofs). Wenn man viele Funktionen mit kleinem lokalem 
Speicherbedarf hat, dann kann die "Stack-Variante" günstiger sein als 
die "Globals-Variante". BTW, "static type var;" ist auch "global".
Ich vermute die "static"-"Idee" stammt aus der 8051-Zeit. Dort sind 
Pointer-Zugriffe ein Horror.

Wenn man dann vom AVR Richtung ARM schaut, der hat so viele breite 
Index-Register zur Verfügung, daß lokal praktisch immer besser ist als 
(viele) globale Variablen. Wenn schon "global/static", dann wenigstens 
keine einzelnen Variablen, sondern Strukturen aus zusammen verwendeten 
Variablen. Dann kennt der Compiler für jede Variable den Offset 
innerhalb der Struktur und braucht (innerhalb gewisser Grenzen, ARM:4k) 
nur eine Basisadresse. Andernfalls: Mem-Adresse relative zum PC aus 
Flash holen und auf genau eine Variable zugreifen. Für jede einzelne 
Variable ein anderes Adressregister, bis diese ausgehen und dann 
eventuell eben mehrfach Adressen laden. Mit bis zu 4K-struct: einmal 
Basisadresse laden und dann Zugriff über Offset.
Selbst wenn man die 32bit nicht zum Rechnen braucht, die 
Adressierungsarten dieser CPU möchten man nicht mehr missen, hat man sie 
mal entdeckt.

von Pandur S. (jetztnicht)


Lesenswert?

Den Speicherverbrauch in den Funktionen kann man auch selbst 
heruasfinden. Wie viele Funktionen tief springt man, und welche 
Kombination hat am meisten Platz in lokalen Variablen deklariert. 
Rekursionen sollten vermieden werden.

: Bearbeitet durch User
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.