Ich würde mich sehr freuen, wenn mir jemand dabei helfen kann,
herausfinden, warum das angehängte Projekt ein so großes Kompilat
erzeugt.
Ich habe das Projekt mit der "System Workbench for STM32" erstellt. Beim
Compilieren erhalte ich folgende Ausgabe:
Vermutlich fehlen mir irgendwelche Compiler oder Linker Optionen, aber
welche? Dazu habe ich wohl noch zu wenig Erfahrung, denn alle meine
Versuche haben die Größe kaum verändert.
Ich bin so ein Esel, ich habe ganz vergessen zu schreiben, wieso ich
glaube dass der Code zu groß ist. Also:
Wenn ich die Zeile
> puts("Hello World!")
auskommentiere, verringert sich .text von etwa 3,5kB auf weniger als 1kB
und .data verringert sich von 112 auf 12.
Der Text ist aber viel kleiner als 100 Bytes und ich kann mir kaum
vorstellen, dass puts() hier satte 2,5KB Code benötigt.m Ich hätte hier
höchstens 200 Bytes Code erwartet.
Ist meine Erwartung falsch oder fehlt irgendein Setting?
Stefan U. schrieb:> Ist meine Erwartung falsch oder fehlt irgendein Setting?
mit dem Einbinden von puts() kommen nicht nur ein paar Bytes, sondern
gleich ein Großteil der C-Library mit.
Wenn Du die brauchst, ist das durchaus in Ordnung (bei zunehmender
Nutzung von Funktionen aus der Library kommt von dort nur noch
unproportional Zuwachs).
Wenn Du sie nicht brauchst (weil Du tatsächlich nur ein paar Strings
ausgeben möchtest), schreib' dir die Routine einfach (ohne Overhead)
selbst.
Danke für Deine Hilfe. Das beantwortet meine Frage.
Falls der große Anhang im Eröffnungsbeitrag zu viel Platz braucht möge
ein Moderator ihn bitte löschen. Es scheint, dass er nicht benötigt
wurde.
Markus F. schrieb:> mit dem Einbinden von puts() kommen nicht nur ein paar Bytes, sondern> gleich ein Großteil der C-Library mit
EIn Großteil ist wohl etwas übertrieben, aber es kommt schon daher. Es
wird nicht nur der Code für pus() eingebunden.
Du kannst das herausfinden, indem du den Mapfile ohne puts() mit dem
Mapfile mit eingebundendem puts() vergleichst. Da kannst du erkennen,
was alles eingebunden wird, wenn du puts() benutzt.
Markus F. schrieb:> mit dem Einbinden von puts() kommen nicht nur ein paar Bytes, sondern> gleich ein Großteil der C-Library mit
Und das ist bei jedem so, nicht nur bei ARM, sondern auch beim Spielzeug
AVR.
-flto fürt zur Fehlermeldung: undefined reference to `_write'
-Os anstelle von -O2 macht das Programm nur wenige Bytes kleiner.
fno-rtti -fno-exceptions bringt auch fast nichts.
Im map File sehe ich eine Menge Funktionen, die ich normalerweise in
Kombination mit Files verwende (lseek, flush, errno) und ein par Sachen
die wohl mit dem Puffer zusammenhängen (den ich am liebsten weglassen
würde). Ich denke das stimmt schon so, dass das alles Abhängigkeiten
sind, die puts() mit sich bringt.
Die 2kB bringen mich jetzt nicht um, aber ich werde das im Hinterkopf
behalten. Sicher ist das auch für die Performance relevant. Das Ausgeben
von Trace Meldungen mit Hilfe von puts() scheint mir jetzt jedenfalls
nicht mehr so attraktiv, wie zuvor. Das programmiere ich doch lieber
selbst eine kleine for-Schleife.
Stefan U. schrieb:> Das programmiere ich doch lieber selbst eine kleine for-Schleife.
Hmmmm..... Ist dein Flash zu 99% voll?
Das lohnt doch sonst garnicht. Ein STM32 hat doch genug Flash.
Stefan U. schrieb:> Sicher ist das auch für die Performance relevant.
Ja .... das ist wahrhaftig schwarze Magie!
Ich würde mich von solch abstrusen Esoterik-Code nicht
ausbremsen lassen.
Stefan U. schrieb:> -flto fürt zur Fehlermeldung: undefined reference to `_write'
Da wird _write wahrscheinlich nie explizit verwendet, sondern immer nur
über einen Funktionspointer in der Tabelle mit den File-Operationen.
Dann denkt sich der Compiler, ach eh' nie verwendet, also weg damit ...
Einfach das _write einmal explizit verwenden sollte helfen.
Das kommt gelegentlich vor, bei ST findet sich in den Demos mal so eine
Mini-Datei:
#include "FreeRTOS.h"
#include "task.h"
void dummyFunction(void) __attribute__((used));
// Never called.
void dummyFunction(void) {
vTaskSwitchContext();
}
Da war's beim vTaskSwitchContext() das gleich Problem wie beim _write.
Stefan U. schrieb:> Die 2kB bringen mich jetzt nicht um, aber ich werde das im Hinterkopf> behalten.
Ich nehme mal an, daß du jetzt eines gelernt hast: Wer sich auf einem
kleinen µC programmiertechnisch genauso benimmt wie auf dem PC, der muß
eben auch damit rechnen, daß entsprechende Laufzeit-Bibliotheken und
Ersatz-Routinen für das, was auf dem PC das Betriebssystem bietet, mit
eingebunden werden müssen.
Das kostet dann eben Speicherplatz und es kostet Performance. Und (nicht
zu vergessen) man muß in vielen Fällen den eigentlichen Lowlevel-Treiber
selber dazuliefern, der sich natürlich in das Interface der
eingebundenen Bibliotheken einpassen muß.
Mir wäre das alles viel zu stressig, weswegen ich für meine Bedürfnisse
auf sowas wie puts oder printf generell pfeife, und mir meine eigenen,
an die Verhältnisse auf dem µC angepaßten E/A-Möglichkeiten geschrieben
habe.
Mittlerweile hab ich davon ja hier genug gepostet. Allerdings muß man
dann eben nicht puts hinschreiben, sondern z.B. String_Out(...). Ob das
dem einen oder anderen unerträglich ist, muß jeder für sich selber
entscheiden.
W.S.
> Was hat die Größe des Codes mit der Performance zu tun?
Viel Code benötigt viele CPU Takte. Die ungenutzten Funktionen sind ja
schon weg optimiert.
> Ich nehme mal an, dass du jetzt eines gelernt hast...
Im Prinzip hast du Recht. Ich habe hier allerdings gestaunt, weil die
puts() Funktion bei AVR samt aller Abhängigkeiten weniger als 500 Bytes
umfasst.
W.S. schrieb:> Mir wäre das alles viel zu stressig, weswegen ich für meine Bedürfnisse> auf sowas wie puts oder printf generell pfeife, und mir meine eigenen,> an die Verhältnisse auf dem µC angepaßten E/A-Möglichkeiten geschrieben> habe.>> Mittlerweile hab ich davon ja hier genug gepostet. Allerdings muß man> dann eben nicht puts hinschreiben, sondern z.B. String_Out(...). Ob das> dem einen oder anderen unerträglich ist, muß jeder für sich selber> entscheiden.
Es ist unerträglich. Deshalb habe ich "mir meine eigenen, an die
Verhältnisse auf dem µC angepaßten E/A-Möglichkeiten geschrieben". Nur
heißen meine Funktionen trotzdem printf() oder strlen(). Was spricht
dagegen?
Bauform B. schrieb:> Nur> heißen meine Funktionen trotzdem printf() oder strlen(). Was spricht> dagegen?
Dass man im Anwendungs-Code keine Bezeichner verwenden sollte, die für
die C-Standardbibliothek reserviert sind. Das gibt ein Chaos wenn jemand
zusätzlich den Standard-Header <stdio.h> inkludiert. Das kann man nur
machen wenn die C Standard Library garantiert nie mit gelinkt und
inkludiert wird. Deutlich einfacher (und standardkonform!) ist es, die
Funktion einfach anders zu benennen (my_printf o.ä.). Das tut auch nicht
weh.
Stefan U. schrieb:> Im Prinzip hast du Recht. Ich habe hier allerdings gestaunt, weil die> puts() Funktion bei AVR samt aller Abhängigkeiten weniger als 500 Bytes> umfasst.
Die AVR-Libc ist nunmal genau auf die Nutzung ohne OS auf
Mini-Controllern abgestimmt. Die vom ARM-GCC genutzte newlib ist mehr
für Unixoide Systeme gemacht und daher nicht immer optimal für kleine
Controller. Alles was mit FILE* arbeitet (fwrite, fprintf, fputs)
braucht Puffer und dafür malloc().
Bauform B. schrieb:> Es ist unerträglich. Deshalb habe ich "mir meine eigenen, an die> Verhältnisse auf dem µC angepaßten E/A-Möglichkeiten geschrieben". Nur> heißen meine Funktionen trotzdem printf() oder strlen(). Was spricht> dagegen?
... bei mir heißen die auch so und deswegen ich sehr häufig um stdio.h
einen großen Bogen und um string.h einen kleineren...
Dr. Sommer schrieb:> Deutlich einfacher (und standardkonform!) ist es, die> Funktion einfach anders zu benennen (my_printf o.ä.). Das tut auch nicht> weh.
: - )
Ich muß jetzt schmunzeln, weil es bei mir genau
my_printf
heißt und ich hierfür unterschiedlich abgespeckte printf Funktionen
habe, die alle in der Main-Datei eine Funktion
my_putchar
benötigen (die von my_printf aufgerufen wird).
Je nach verwendetem Controller gibts dann von printf Versionen ohne
Kommaausgabe, eine mit Festkomma und eine mit Fließkomma.
Somit kann man dann sogar noch relativ komfortabel Ausgaben selbst auf
einem STM32F030 mit nur 16 kByte vornehmen.
Dr. Sommer schrieb:> Bauform B. schrieb:>> Nur>> heißen meine Funktionen trotzdem printf() oder strlen(). Was spricht>> dagegen?> Dass man im Anwendungs-Code keine Bezeichner verwenden sollte, die für> die C-Standardbibliothek reserviert sind. Das gibt ein Chaos wenn jemand> zusätzlich den Standard-Header <stdio.h> inkludiert.
Wie reserviert sind die wirklich? Warum weiß ich das nicht? Aber davon
abgesehen dürfte es kein Chaos geben, wenn man dabei meine Header-Datei
nicht inkludiert. So kompatibel sollten die eigenen Funktionen
gefälligst sein.
Selbst wenn man die eigenen Funktionen und die newlib dazu linkt,
müsste es eigentlich noch funktionieren. printf ist dann schon definiert
und dürfte nicht aus der newlib geholt werden.
Aber eigentlich ist es viel einfacher: die C-Standardbibliothek kann
ja glibc, dietlibc oder newlib heißen. Genauso gut kann man eine eigene
verwenden.
> Alles was mit FILE* arbeitet (fwrite, fprintf, fputs)> braucht Puffer und dafür malloc().
Eine eigene libc könnte statische Puffer pro physikalisch vorhandener
Schnittstelle haben. Andere Kanäle zu öffnen gibt so oder so einen
Fehler.
All identifiers with external linkage in any of the following subclauses (including the future library directions) and errno are always reserved for use as identifiers with external linkage.
2
3
If the program declares or defines an identifier in a context in which it is reserved (other than as allowed by 7.1.4), or defines a reserved identifier as a macro name, the behavior is undefined.
D.h. ohne "static" darf man keine eigenen Bezeichner namens "printf"
o.ä. definieren, sonst passiert irgendwas. Selbst mit "static" ists
problematisch, falls man mal versehentlich/indirekt stdio.h inkludiert.
Bauform B. schrieb:> Warum weiß ich das nicht?
Standard nicht gelesen?
Bauform B. schrieb:> Selbst wenn man die eigenen Funktionen und die newlib dazu linkt,> müsste es eigentlich noch funktionieren. printf ist dann schon definiert> und dürfte nicht aus der newlib geholt werden.
Obiges Zitat verbietet dies.
Bauform B. schrieb:> Genauso gut kann man eine eigene> verwenden.
Ja, da darf man dann alles machen. Ist dann halt Standard-Library-Code
und nicht Anwendungs-Code.
Ich verstehe auch den Nutzen nicht, warum man unbedingt eine Funktion
namens printf definieren muss - man nenne sie einfach my_printf oder so,
und alles ist gut. Warum lange überlegen ob und wann "printf" erlaubt
ist, wenn "my_printf" definitiv erlaubt ist und funktioniert...
Dr. Sommer schrieb:> Ich verstehe auch den Nutzen nicht, warum man unbedingt eine Funktion> namens printf definieren muss - man nenne sie einfach my_printf oder so,> und alles ist gut.
Wenn man das Programm liest, weiß man sofort was printf macht. Bei
my_printf wird man evt. nachschauen. Das finde ich schon nützlich.
> Dann überschreibe doch den _write "Syscall" Stub der libc, um stdout> umzuleiten
Und schon drehen wir uns im Kreis, denn genau damit hat derb Thread
angefangen.
Dann kommt man auf über 2,5kB Code-Größe für puts() (+dependencies),
während es beim avr-gcc nur etwa 500 bytes kostet.
Ich habe gemessen, wie viel Speicher die Funktionen puts() und printf()
bei der newlib-nano auf einem STM32F103 ohne Fließkomma-Unterstützung
benötigen:
Der Speicherbedarf von printf() ist unabhängig von der Anzahl der
Argumente und Formatier-Optionen. Wenn weniger als 1468 Bytes Heap zur
Verfügung stehen, belegt die Library stattdessen nur 436 Bytes und gibt
dann jedes Zeichen einzeln mit _write() aus. Wenn weniger als 436 Bytes
Heap zur Verfügung stehen brechen die Funktionen mit einer HardFault
Exception ab.
Verglichen mit der avr-libc ist das hammerhart viel, ich bin echt
geschockt! Dabei ist die newlib-nano doch schon speziell für µC
optimiert. Ichtraue mich gar nicht, die selbe Messung nochmal mit der
"großen" newlib zu wiederholen.
Ich hatte auf einem M0 das printf mit Integer durch ein vereinfachtes
itoa ersetzt, das hat immer noch ca. 2 kB statt 3,etwas gebraucht. Die
Integer Division scheint viel zu kosten, wobei der M3 das ja schon in HW
kann.
Wäre interessant zu sehen was der Keil daraus macht, der soll ja besser
optimieren.
Die Newlib haut richtig rein, ist dafür Thread safe wenn es in Richtung
RTOS geht.
Stefan U. schrieb:> ich bin echt geschockt!
So ein STM32F103 hat 20 KB RAM. Wenn Du nicht bereit bist, 1,5KB davon
abzuzweigen, hast Du 2 Möglichkeiten:
1. Auf puts/printf verzichten
2. Den offensichtlich satten Speicherverbrauch Deiner Applikation
der verbliebenen 18 KB zu optimieren.
Dein Programm braucht keine 18 KB? Dann ist's auch kein Problem.
Frank M. schrieb:> Dein Programm braucht keine 18 KB? Dann ist's auch kein Problem.
Sehe ich auch so. Für nicht verbrauchten Speicher gibt's kein Geld
zurück ;)
Man muss nicht mit Speicher rumasen aber wenn er eh da ist, fange ich an
mir Gedanken zu machen wenn es kneift und nicht sehr viel früher.
Stefan U. schrieb:> hammerhart viel, ich bin echt> geschockt!
wem das nicht passt, der muss eben sein xprintf() (oder was auch immer)
selber schreiben. Wenn man auf ein paar exotische Formatierungen (wie %n
oder %a, z.B.), die sowieso keiner braucht, verzichtet, geht das in
weniger als 1500 Bytes.
900ss D. schrieb:> Man muss nicht mit Speicher rumasen aber wenn er eh da ist, fange ich an> mir Gedanken zu machen wenn es kneift und nicht sehr viel früher.
Ack, trotzdem ist es gut zu wissen wieviel etwas kostet. Beim Vergleich
mit AVR sollte man sehen das der AVR viel in einem Byte erledigen kann
und der ARM da zwei braucht. Aber dafür gibts dann auch devices mit bis
zu 2 MByte Flash.
>"-Os", "-flto"
Das sind nur Compiler-Optionen und werden bei dem betreffenden Problem
nicht helfen, da die Lib durch den Linker eingebunden wird. Also nach
dem kompillieren des Codes.
Allerdings gibt es für den Linker auch Optimierungs-Optionen. Eine davon
nennt sich glauch "garbage collect unused ....", die entfernt alle nicht
verwendeten Symbole aus dem Object Code. Damit sollten alle nicht
verwendeten Funktionen der Lib nicht in den Programmcode übernommen
werden.
Die obige Info gilt nur für GCC. Andere Compiler/Linker dürften aber
ähnliche Optionen haben.
Wobei 2K für ein puts() schon ordentlich ist. Evtl. kann ein Blick in
die Lib-Sourcen (falls verfügbar) nicht schaden. Ich vermute mal, dass
die puts()auch nur putchar() in einer Schleife aufruft. Da sollten
eigentlich keine 100 Funktionen verschachtelt sein ;-)
Beim printf() sieht das schon etwas anders aus, wegen der versch.
Subroutinen für die Zahlenkonvertierung.
Matthias schrieb:> Wobei 2K für ein puts() schon ordentlich ist. Evtl. kann ein Blick in> die Lib-Sourcen (falls verfügbar) nicht schaden. Ich vermute mal, dass> die puts()auch nur putchar() in einer Schleife aufruft.
Es ist wie gesagt das ganze Puffer-Handling mit dabei, und deswegen halt
auch malloc(). Die newlib ist nunmal nicht für kleinste Controller
optimiert, auhc nicht die newlib-nano. Wenn man unbedingt aufs letzte
Byte optimieren muss, sucht man sich eine andere libc. Oder verzichtet
auf printf - wozu braucht man das nochmal unbedingt bei super
performance-kritischen Anwendungen?
>> hammerhart viel, ich bin echt geschockt!> wem das nicht passt, der muss eben sein xprintf()> (oder was auch immer) selber schreiben.
Ich spiele mit dem Gedanken, etwas aus der avr-libc zu kopieren. Die
dortige Implementierung kommt mit etwa 100 Bytes RAM aus.
Was mich bei dem Speicherverbrauch etwas stört ist:
Hier im Forum wird von bestimmten Leuten immer wieder gepredigt "Nimm
besser einen ARM", da bekommst du auch mehr Speicher zum selben Preis.
Nun stelle ich aber fest, dass sowohl der Flash Verbrauch als auch der
RAM Verbrauch erheblich höher sind, als bei AVR. Und das gilt nicht nur
für printf, sondern zieht sich durch die gesamte Programmierung.
Zuerst hieß es: "Ja aber, die Interrupt-Vektor Tabelle darfst du nicht
mitzählen". Dann hieß es: "Die Initialierung machst du ja nur einmal,
die darfst du nicht mit zählen". Dann wiederum "Zugriff auf USB und RTC
ist doof aber macht man ja nicht ständig" (sicher?). Und jetzt setzt
printf() dem noch eine große Krone obendrauf.
Und dort endet es nicht. Auch mein eigener Code belegt auf ARM sehr viel
mehr Speicher, als auf AVR. Und zwar ungefähr das doppelte.
Das viel gesagte Argument: "Nimm ARM, dann hast du mehr Speicher" gilt
für mich nur noch für die großen Modelle. Einen 128kB AVR durch einen
256kB ARM auszutauschen wäre in dieser Hinsicht nutzlos.
Jetzt mal meine ganz ehrliche Meinung, nach 1 Jahr Evaluierung: Die AVR
sind mir immer noch sympathischer.
Stefan U. schrieb:> Das viel gesagte Argument: "Nimm ARM, dann hast du mehr Speicher" gilt> für mich nur noch für die großen Modelle. Einen 128kB AVR durch einen> 256kB ARM auszutauschen wäre in dieser Hinsicht nutzlos.>> Jetzt mal meine ganz ehrliche Meinung, nach 1 Jahr Evaluierung: Die AVR> sind mir immer noch sympathischer.
Hast Du ein konkretes ARM-Projekt, bei dem Du wirklich an die Grenzen
des Speichers gerätst?
Ansonsten ist das nur Rumgeheule.
Markus F. schrieb:> Ansonsten ist das nur Rumgeheule.
... und Erbsenzählerei. Nach einem Jahr Beschäftigung mit ARM (hier geht
es wohl konkret um STM32Fxxx) sollte man die unterschiedliche
Leistungsklasse der beiden Familien eigentlich begriffen haben.
Stefan U. schrieb:> Die AVR> sind mir immer noch sympathischer.
Dann nimm sie doch einfach. Bleibe bei 8 Bit Variablen, float/double ist
böse und langsam und printf() für alle Zeit tabu.
So möchte ich allerdings nicht mehr programmieren wollen.
Stefan U. schrieb:> Ich spiele mit dem Gedanken, etwas aus der avr-libc zu kopieren. Die> dortige Implementierung kommt mit etwa 100 Bytes RAM aus.
Dein Irrtum ist einfach, unbewusst eine Bibliothek mit in Dein Programm
zu ziehen, die Du gar nicht brauchst. Nein, ich rede nicht von printf(),
sondern von stdio.
printf(...) ist nichts anderes als fprintf (stdout, ...), puts(s) ist
nichts anderes als fputs (s, stdout).
Damit ziehst Du eine komplette, komplexe, aber auch geniale
Abstraktionsschicht in Dein Programm, welche IO für beliebige Geräte -
egal ob zeichen- oder blockorientiert - auf gepufferte Ein- und
Ausgabeströme transferiert. Dazu gehört auch das Interface mit
Filepointern, welche intern auf Fildeskriptoren gemappt werden und
letztendlich in die oben bereits erwähnten "Syscall" Stubs der libc
führen.
Kein Wunder, so eine komfortable Lib kostet auch was!
Denn hier kannst Du mehrere Ein- und Ausgabeströme über verschiedene
Filepointer gleichzeitig handeln. Das ist wesentlich mehr, als die
avr-libc kann.
Warum machst Du das, wenn Du stdio gar nicht brauchst? Nimm nicht
printf(), sondern sprintf(). Nimm nicht puts(), sondern Dein eigenes
uart_puts()! Und schon lösen sich Deine "Platzprobleme" von selbst.
Nochmal:
> Ich spiele mit dem Gedanken, etwas aus der avr-libc zu kopieren. Die> dortige Implementierung kommt mit etwa 100 Bytes RAM aus.
Das, was Du da kopieren willst, ist kein "echtes" stdio. Die avr-libc
kennt genau einen Ausgabestrom, die STM32-libc kennt wesentlich mehr.
Also: Wenn Du kein stdio brauchst, dann lass es auch. sprintf()
existiert.
EDIT:
Hier ein kleines Modul, mit dem Du ein eigenes printf() ohne stdio
realisieren kannst:
Stefan U. schrieb:> Und jetzt setzt printf() dem noch eine große Krone obendrauf.
Und die ARM Architektur ist schuld daran, dass du eine suboptimale libc
verwendest? Kaufe eine optimierte libc, z.B. die von Keil, und du kannst
die paar Bytes sparen. Auf ARM.com gibt es irgendwo einen Vergleich, wie
viel weniger Programm Code ARM Thumb2 im Vergleich zu 8051 braucht.
Und
Stefan U. schrieb:> Dann wiederum "Zugriff auf USB und RTC ist doof aber macht man ja nicht> ständig"
Wie "doof"? Ist die USB Peripherie von den AVRs so viel besser? Und
welche von denen haben eine RTC?
Stefan U. schrieb:> Und dort endet es nicht. Auch mein eigener Code belegt auf ARM sehr viel> mehr Speicher, als auf AVR. Und zwar ungefähr das doppelte.
Dann optimiere deinen ARM Code auch bitte genauso aufwendig wie deinen
AVR Code, also jeder Register Zugriff einzeln usw. Allein schon dass man
beim AVR jede String Funktion doppelt braucht (printf und printf_P)
treibt den Flash Verbrauch in die Höhe.
Stefan U. schrieb:> Und dort endet es nicht. Auch mein eigener Code belegt auf ARM sehr viel> mehr Speicher, als auf AVR. Und zwar ungefähr das doppelte.
Andere machen andere Erfahrungen. Bei Chans FatFs kommt ehrer das
Gegenteil raus.
http://elm-chan.org/fsw/ff/doc/appnote.html