Hallo zusammen, natürlich verwende ich (bewusst) keine dynamische Speicherverwaltung und entdecke aber gerade via avr-nm, dass die Funktionen malloc() und free() enthalten sind und jeweils unnötig Speicher fressen (304 + 274 bytes). Ich suche mich gerade tot im Internet (-dead_strip etc.), finde aber absolut keinen Tipp, wie man die Funktionen los wird. Eine leere Implementierung funktioniert nicht. Auch die gcc-Flags "-why_live symbol_name" funktionieren beim AVR nicht. Ich vermute ja irgendeine dumme Abhängigkeit zu irgendeiner Standard-Funktion... Hat jemand irgendein Tipp? Viele Grüße Andreas
Nein, ich verwende eigentlich nur memcpy und memset sonst nichts. Und (noch) float-Funktionen...
Meine Includes sind: <string.h> <util/delay.h> <avr/io.h> <avr/interrupt.h>
Andreas J. schrieb: > natürlich verwende ich (bewusst) keine dynamische Speicherverwaltung und > entdecke aber gerade via avr-nm, dass die Funktionen malloc() und free() > enthalten sind und jeweils unnötig Speicher fressen (304 + 274 bytes). Wenn sie enthalten sind, werden sie genutzt, wenn auch über Bande. Und ihr Speicherverbrauch ist dann nicht unnütz. > Ich vermute ja irgendeine dumme Abhängigkeit zu irgendeiner > Standard-Funktion... > > Hat jemand irgendein Tipp? Disassembliere dein .elf Binary: "avr-objdump -d -S main.elf". Wenn du mit Debug-Symbolen compiliert hast (es gibt keinen Grund, der dagegen spräche) hast du nachher an jedem rcall den Namen der aufgerufenen Funktion als Kommentar. Und dann kannst du dich rückwärts durchhangeln, welche Funktion(alität) da auf malloc() und free() beharrt. Nochwas. Ich habe mittlerweile eigentlich immer die folgenden Optionen in meinen CFLAGS: "-fdata-sections -ffunction-sections -flto -Wl,--gc-sections". Die Bedeutlung solltest du selber nachschlagen.
Super, Danke Dir für den Tipp! malloc() wird nur von atexit() aufgerufen. Und aus irgendeinem Grund ruft der Compiler am Ende des Konstruktors: static TinyController controller(serial); ... die Funktion atexit() auf. Die einzige Stelle. Da ich auch __cxa_guard_acquire etc. loswerden will, entsorge ich gerade alle pure virtual functions. Reicht aber nicht, sie werden auch durch den Konstruktor erzwungen. Ich probiere mal weiter...
Wenn ich static (s.o.) weglasse, ist auch atexit() und damit malloc() und free() weg. Leider habe ich jetzt den Nachteil, dass der tatsächlichen Speicherverbrauch (Data Memory Usage) nicht mehr korrekt ist, da der Compiler den controller (s.o) nicht mehr "sieht"...
Versuche mal folgende Compiler-Optionen: -fno-use-cxa-atexit -ffunction-sections -fdata-sections -flto Und folgende Linker-Optionen -Wl,--gc-sections -flto Das könnte die Funktionen wegoptimieren. Definiere eine leere Funktion "__cxa_pure_virtual" um die Behandlung rein virtueller Funktionen loszuwerden.
Andreas J. schrieb: > Leider habe ich jetzt den Nachteil, dass der tatsächlichen > Speicherverbrauch (Data Memory Usage) nicht mehr korrekt ist, da der > Compiler den controller (s.o) nicht mehr "sieht"... Wie passt das zusammen? Wo ist die Instanz angelegt, in der main()?
Hilft leider nicht... Sobald ich meine C++ - Instanz statisch mache, ziehe ich mir atexit() und damit malloc() rein. Das wird der implizite statische Destruktor sein. Ist sie nicht statisch, dann generiert der Compiler ja auch einen impliziten Destruktor, bei diesem ist aber offensichtlich kein atexit() notwendig, und das ist jetzt meine Lösung. Ich müsste dem Compiler sagen können, dass er keine (statischen) Destruktoren generieren soll, da wir ja eh eine Endlosschleife in main() haben. Ich brauche überhaupt keine Destruktoren... Aber ob es dafür ein Flag gibt? Ich finde jedenfalls keins...
Dr. Sommer schrieb: > Wie passt das zusammen? Wo ist die Instanz angelegt, in der main()? Ja in main.
1 | #include "Kernel/Clock.h" |
2 | #include "Kernel/Serial.h" |
3 | #include "AqMod/TinyController.h" |
4 | |
5 | #include <avr/interrupt.h> |
6 | #include <avr/io.h> |
7 | |
8 | using namespace AJ::Kernel; |
9 | using namespace AJ::AqMod; |
10 | |
11 | extern "C" void __cxa_pure_virtual() |
12 | {
|
13 | while (true); |
14 | } |
15 | |
16 | __extension__ typedef int __guard __attribute__((mode (__DI__))); |
17 | |
18 | extern "C" int __cxa_guard_acquire(__guard* g) |
19 | {
|
20 | return !*(char *)(g); |
21 | } |
22 | |
23 | extern "C" void __cxa_guard_release(__guard* g) |
24 | {
|
25 | *(char *)g = 1; |
26 | } |
27 | |
28 | extern "C" void __cxa_guard_abort(__guard*) |
29 | {
|
30 | } |
31 | |
32 | int main(void) |
33 | {
|
34 | //byte resetReason = MCUSR; |
35 | //MCUSR = 0x00; |
36 | |
37 | Clock::Initialize(); |
38 | static Serial serial(EBaudRate::BR9600); |
39 | static TinyController controller(serial); |
40 | |
41 | sei(); |
42 | |
43 | while (true) |
44 | {
|
45 | controller.Loop(); |
46 | } |
47 | } |
Program Memory Usage : 15624 bytes 95,4 % Full Data Memory Usage : 740 bytes 72,3 % Full EEPROM Memory Usage : 96 bytes 18,8 % Full
1 | #include "Kernel/Clock.h" |
2 | #include "Kernel/Serial.h" |
3 | #include "AqMod/TinyController.h" |
4 | |
5 | #include <avr/interrupt.h> |
6 | #include <avr/io.h> |
7 | |
8 | using namespace AJ::Kernel; |
9 | using namespace AJ::AqMod; |
10 | |
11 | extern "C" void __cxa_pure_virtual() |
12 | {
|
13 | while (true); |
14 | } |
15 | |
16 | int main(void) |
17 | {
|
18 | //byte resetReason = MCUSR; |
19 | //MCUSR = 0x00; |
20 | |
21 | Clock::Initialize(); |
22 | Serial serial(EBaudRate::BR9600); |
23 | TinyController controller(serial); |
24 | |
25 | sei(); |
26 | |
27 | while (true) |
28 | {
|
29 | controller.Loop(); |
30 | } |
31 | } |
Program Memory Usage : 14860 bytes 90,7 % Full Data Memory Usage : 287 bytes 28,0 % Full EEPROM Memory Usage : 96 bytes 18,8 % Full
Mach die Instanzen einfach global, vor das main(), egal ob mit static oder ohne. So wird das Speicher im .data Segment gezählt, und der Destruktor wird nie aufgerufen, und du brauchst die Locks nicht.
Die Instanzen global zu machen hatte ich mir auch schon überlegt (musste nur auf Late Binding umstellen, ist aber eh sauberer)... Und merkwürdigerweise generiert der Compiler auch bei static keinen Destruktor mit atexit() mehr. Wobei ich static jetzt natürlich weglasse, das war ja nur ein "Trick", den echten Speicherverbrauch (trotz Instanzzierung in main()) zu sehen... Danke Dir, wieder was gelernt ;) !
Andreas J. schrieb: > Und merkwürdigerweise generiert der Compiler auch bei static keinen > Destruktor mit atexit() mehr. Weil static bei globalen Variablen eine komplett andere Bedeutung hat, es verhindert einfach nur den Zugriff auf die Variable aus anderen Source-Files, denn die Variable erhält kein Symbol mehr. static innerhalb von Funktionen impliziert eine relativ komplexe Logik mithilfe von Locks die das Objekt erst dann anlegt, wenn die Funktion zum ersten Mal aufgerufen wird.
Ach, na klar hat static dann ne andere Bedeutung (ist ja peinlich, dass ich das vergessen habe :-)). Und der zweite Teil erklärt auch, warum ich __cxa_guard_acquire() und co. nicht mehr brauche... Danke nochmal!
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.