Hallo zusammen, zum Aneignen/Vertiefen von Programmierkenntnissen entwickle ich gerade privat an einem Projekt rum. Verwendeter Mikrocontroller: STM32F103C8T6. Ich habe einen funktionstüchtigen Versuchsträger der ausschließlich in C programmiert ist. Ich will nun das Projekt auf objektorientiertes C++ umstellen. Dafür habe ich nun einen Teil der Software nun als Klasse programmiert, und sie funktioniert auch wunderbar. Jedoch scheint sich die ROM-Auslastung drastisch zu erhöhen. Könnt ihr mir Tipps geben, wie ich ROM freischaufeln kann? Anbei findet ihr: - robot-ausschlieszlich-c.map: MAP-File der C-only-Lösung - robot-mit-cplusplus-anteilen.map: MAP-File der erweiterten Lösung bei der das Modul "Trajectory" als C++-Klasse implementiert wurde. - stm32f103c8_flash.ld: Linkerskript. Ich denke, dass das __etext-Symbol das Ende des benutzten ROMs markiert. Wenn das stimmt, sind bei der C++-Lösung 61k der 64k ROM belegt. Bei der C-only-Lösung sind nur runde 25k belegt. Gibt es etwas das ich tun kann, um die ROM-Belegung zu reduzieren? Folgende Switches habe ich gesetzt: - isolate each function in a section - remove unused sections - don't catch exceptions - no run-time-type-identification - newlib nano-branch - use C++ libraries - no optimizations (Für die Entwicklungsphase würde ich das gerne beibehalten, da optimierter Code angeblich schwerer zu debuggen ist.) Vielen Dank im Vorraus! Manuel
Manuel W. schrieb: > - no optimizations (Für die Entwicklungsphase würde ich das gerne > beibehalten, da optimierter Code angeblich schwerer zu debuggen ist.) Das ist halt schon verkehrt. Optimierungen ausschalten und guten/kleinen Code erwarten macht halt keinen Sinn. Schalte mal -Os ein. Wenn du tatsächlich Probleme mit Debugging hast, verwende -Og oder doch -O0. Ansonsten: https://www.mikrocontroller.net/articles/ARM_GCC#Code-Gr.C3.B6.C3.9Fe_optimieren befolgen Disassembly posten, dann kann man schauen was da viel frisst
Erst mal ueberlegen was C++ denn bringt. Sie sollte die Dokumentaion wegen der Re-use verbessern, man bringt eine Struktur in die Dokumentation die vorher nicht da war. Und sie sollte den Code dichter machen, wegen der Re-use von Code. Wenn du also mehr Codespeicher benoetigst ist etwas noch nicht ganz optimal. Das bedeutet, dass zB Debugcode dabei ist, oder Ueberpruefungen. Ich setze mal voraus, du verwendest keine dynamischen zur Laufzeit verlinkten Objekte, und keine dynamischen Objekte. Die beiden sollte man nicht verwenden, denn sie sind fehleranfaellig. Machen den Code viel unzuverlaessiger.
-ffunction-sections -fdata-sections mit --gc-sections mal ausprobiert?
ягт ден троль раус schrieb: > Ich setze mal voraus, du verwendest keine dynamischen zur Laufzeit > verlinkten Objekte, und keine dynamischen Objekte. Die beiden sollte man > nicht verwenden, denn sie sind fehleranfaellig. Machen den Code viel > unzuverlaessiger. Ich habe mein Objekt tatsächlich dynamisch zur Laufzeit instanziiert. Ich weiß, dass das den Code fehleranfälliger macht, aber ich wollte es trotzdem einmal ausprobieren, einfach um es mal gemacht zu haben. Ich habe dynamische Speicherallokation noch niemals benutzt. Ich habe den Zeitpunkt der Instanziierung nun zur Kompilierzeit hin verschoben, und voilà, tatsächlich schrumpfte die Codegröße wieder beträchtlich! Sie betrug dann nur noch 29kB, 3kB mehr als mit reinem C. Das hätte ich mir nicht gedacht. Was ist der Grund dafür? Weil die Notwendigkeit für's Heap-Management entfällt? Etwas übermütig geworden habe mich nach diesem Erfolgserlebnis gleich daran gemacht das nächste Modul auf .cpp umzustellen. Und zwar die Hauptdatei des Projekts, also jene in der main() definiert wird. Obwohl da drinnen nichts objektorientiert ist und gar nichts dynamisch stattfindet (kein malloc() oder operator new), ging die Codegröße gleich wieder auf ~60kB hoch. Anbei sind die zugehörigen Dateien: - robot-mit-cplusplus-anteilen-noheap.map: Codegröße 29kB - robot-mit-cplusplus-anteilen-noheap-trotzdem-grosz.map: Codegröße ~60kB - robot-mit-cplusplus-anteilen-noheap-trotzdem-grosz.objdump: Codegröße ~60kB
:
Bearbeitet durch User
Manuel W. schrieb: > Was ist der Grund dafür? Weil die Notwendigkeit für's Heap-Management > entfällt? Die malloc Funktion und das zugehörige Zeug ist halt groß. Außerdem natürlich der Exception Handling Code wenn du new verwendest. Deine Disassembly enthält jede Menge double Berechnungen. Vielleicht mal auf float oder Interesse umstellen. Außerdem Exception Handling Code. Der wird trotz -fno-exceptions eingebunden wenn du irgendwas tust, was Exceptions generieren kann. Überschreibe mal __cxa_pure_virtual
Ich kann in meiner IDE auf newlibnano umstellen, da sinkt der benötigte Speicher im Flash um 50% bei mir.
Dr. Sommer schrieb: > Manuel W. schrieb: >> Was ist der Grund dafür? Weil die Notwendigkeit für's Heap-Management >> entfällt? > > Die malloc Funktion und das zugehörige Zeug ist halt groß. Außerdem > natürlich der Exception Handling Code wenn du new verwendest. Hmm... Alles klar. Interessant, was da alles hineingeschwemmt wird. > Deine Disassembly enthält jede Menge double Berechnungen. Vielleicht mal > auf float oder Interesse umstellen. Außerdem Exception Handling Code. > Der wird trotz -fno-exceptions eingebunden wenn du irgendwas tust, was > Exceptions generieren kann. Überschreibe mal __cxa_pure_virtual Das alles wundert mich ehrlichgesagt ziemlich. - type float verwende ich im Code durchaus, aber type double habe ich wirklich nirgendwo verwendet. - Den Einfluss meiner Änderung auf's Exception-Handling verstehe ich auch nicht ganz. Ich habe wirklich nur mein .c-File in .cpp umbenannt, den Targetcompiler geändert (CPP anstatt CC), ein paar Compilerswitches gesetzt (zB "don't catch exceptions") und eingebundenen C-Header über extern "C" geklammert. Ich instanziiere aber keine Objekte oder ähnliches. Da drin ist nur stinknormaler C-Code. Das mit __cxa_pure_virtual probiere ich mal.
Dr. Sommer schrieb: > Vielleicht mal auf float oder Interesse umstellen. LOL. Der Korrektur-Automat lässt grüßen!
Ich habe nun - __cxa_pure_virtual definiert - und folgenden Artikel gefunden: http://www.embedded.com/design/mcus-processors-and-socs/4007134/Building-Bare-Metal-ARM-Systems-with-GNU-Part-4 Grund dafür: Die Codebeispiele hinter folgenden Absätzen habe ich hinzugefügt. "Most low-end ARM-based MCUs cannot tolerate 50KB code overhead. To eliminate that code you need to define your own, non-throwing versions of global new and delete, which is done in the module mini_cpp.cpp located in the directory cpp_blinky1." "Finally, if you don't use the heap, which you shouldn't in robust, deterministic applications, you can reduce the C++ overhead even further. The module no_heap.cpp provides dummy empty definitions of malloc() and free():"
1 | #include <stdlib.h> // for prototypes of malloc() and free() |
2 | |
3 | // ............................... |
4 | extern "C" void *malloc(size_t) { |
5 | return (void*)0; |
6 | } |
7 | |
8 | // ............................... |
9 | extern "C" void free(void *) { |
10 | } |
11 | |
12 | // ................................................................. |
13 | void *operator new(size_t size) throw() { return malloc(size); } |
14 | // ................................................................. |
15 | void operator delete(void *p) throw() { free(p); } |
16 | // ................................................................. |
17 | extern "C" int __aeabi_atexit( |
18 | void *object, |
19 | void (*destructor)(void *), |
20 | void *dso_handle) |
21 | { |
22 | return 0; |
23 | } |
24 | |
25 | void __cxa_pure_virtual() |
26 | { |
27 | |
28 | } |
Leider ist die Codegröße nach wie vor bei ~60kB...
Vielleicht hilft Dir dieser Link: https://www.gitbook.com/book/arobenko/bare_metal_cpp/details Ansonsten, wenn möglich auf den heap verzichten, RTTI und exceptions ausschalten. Dann gibt es eigentlichen keinen Grund, warum Deine C++ Variante größer sein sollte. Vielleicht fängst Du mal damit an, Dein C code mit einem C++ compiler übersetzbar zu machen. Dann ist offensichtlich, dass sich die Code-Größen nicht unterscheiden dürften. mfg Torsten
Ich habe jetzt das Problem gefunden! Ich musste einfach nur Definitionen für die symbols __cxa_end_cleanup und __gxx_personality_v0 hinzufügen.
1 | extern "C" void __cxa_end_cleanup(){}; |
2 | extern "C" void __gxx_personality_v0(){}; |
Wie bin ich darauf gekommen? Wenn man sich die bereits geposteten map files - robot-mit-cplusplus-anteilen-noheap.map - robot-mit-cplusplus-anteilen-noheap-trotzdem-grosz.map näher ansieht, springt es einem eigentlich eh gleich in's Auge. Ganz zu Beginn des Dokuments beschreibt der Linker nämlich, welche Objekte er einbindet, und welche Referenzen der Grund dafür sind:
1 | c:/program files (x86)/emblocks/2.30/share/em_armgcc/bin/../lib/gcc/arm-none-eabi/4.7.3/../../../../arm-none-eabi/lib/armv7-m\libstdc++_n.a(eh_arm.o) |
2 | obj\release\src\main.o (__cxa_end_cleanup) |
Ein Diff macht klar, dass bei der C++-Lösung deutlich mehr Objekte hineingelinkt werden. Object A referenziert ein Symbol aus Object B, welches ein Symbol aus Object C referenziert usw. usf. An der Wurzel dieser ganzen Abhängigkeiten stehen jedoch nur zwei Referenzierungen: Die von __cxa_end_cleanup und __gxx_personality_v0. Wenn ich also für diese selbst Definitionen bereitstelle, wird das Reinlinken der tausend anderen Objects auch verhindert. Die Codegröße ist jetzt wieder auf dem Niveau der C-Lösung. :-) Definitionen für operator new, operator delete, malloc() und delete() bereitzustellen war -- in meinem Fall zumindest -- nicht nötig. Vielleicht kommt das aber noch.
:
Bearbeitet durch User
Torsten R. schrieb: > Vielleicht hilft Dir dieser Link: > https://www.gitbook.com/book/arobenko/bare_metal_cpp/details Danke für den Link, das Buch ist sehr interessant! Torsten R. schrieb: > Ansonsten, wenn möglich auf den heap verzichten, RTTI und exceptions > ausschalten. Dann gibt es eigentlichen keinen Grund, warum Deine C++ > Variante größer sein sollte. > > Vielleicht fängst Du mal damit an, Dein C code mit einem C++ compiler > übersetzbar zu machen. Dann ist offensichtlich, dass sich die > Code-Größen nicht unterscheiden dürften. Genau das mache ich doch gerade...
:
Bearbeitet durch User
> - type float verwende ich im Code durchaus, aber type double habe ich > wirklich nirgendwo verwendet. Da reicht schon ein: 3.1415926 wo eigentlich ein: 3.1415926F hinmuesste.
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.