ROM-Auslastung von C++-Programm reduzieren

OP #4681550
Lesenswert?

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
Gast #4681568
Lesenswert?

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
Gast #4681663
Lesenswert?

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.
OP #4681863
Lesenswert?

ягт ден троль раус 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
Gast #4681918
Lesenswert?

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
OP #4682214
Lesenswert?

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.
OP #4682286
Lesenswert?

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...
(Firma: Torrox.de) #4682313
Lesenswert?

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
OP #4682882
Lesenswert?

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.
OP #4682885
Lesenswert?

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...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren