Im Internet habe ich viel zum Thema "Arduino RAM sparen mit PROGMEM und
strcpy_P()" gefunden. Daher hier nur ganz konkrete Detailfragen, die
eben nicht klar beantwortet werden. Zunächst zwei Code-Beispiele in C:
1
voiddoSomething(charfall){
2
charcommand[45]="Dies ist ein zu modifizierendes Textgerippe\n";
3
4
// verändere jetzt etwas am String command, bevor er weiterverarbeitet wird
5
...
6
}
Nun die RAM-sparende Variante:
1
voiddoSomething(charfall){
2
conststaticcharCmd[]PROGMEM="Dies ist ein zu modifizierendes Textgerippe\n";
3
charcommand[45];
4
strcpy_P(command,Cmd);
5
6
// verändere jetzt etwas am String command, bevor er weiterverarbeitet wird
7
...
8
}
Fragen dazu:
1) Stimmt es, dass beim 1. Programm der String vor main() zur Laufzeit
ins Ram kopiert wird und dann beim Aufruf der Funktion doSomething()
Platz auf dem Stack für command[] resierviert wird und zu Beginn der
Funktion der String aus dem Ram an diesen Platz kopiert wird?
2) Stimmt es, dass beim 2. Programm beim Funktionsaufruf von
doSomething() Platz auf dem Stack für command[] resierviert wird und
dann nur mittels strcpy_P() direkt aus dem Flash in diesen Stack-Platz
kopiert wird?
3) Wieviel länger braucht auf dem ATmega328 strcpy_P() als das Kopieren
in 1)? 1 Taktzyklus pro Zeichen?
Ardur schrieb:> 1) Stimmt es, dass beim 1. Programm der String vor main() zur Laufzeit> ins Ram kopiert wird und
Ja, denn das ist eine statische, lokale Variable.
> dann beim Aufruf der Funktion doSomething()> Platz auf dem Stack für command[] resierviert wird und zu Beginn der> Funktion der String aus dem Ram an diesen Platz kopiert wird?
Nein, denn die Variable ist statisch, sie ist schon dauerhaft angelegt,
siehe oben.
> 2) Stimmt es, dass beim 2. Programm beim Funktionsaufruf von> doSomething() Platz auf dem Stack für command[] resierviert wird und> dann nur mittels strcpy_P() direkt aus dem Flash in diesen Stack-Platz> kopiert wird?
Ja.
> 3) Wieviel länger braucht auf dem ATmega328 strcpy_P() als das Kopieren> in 1)? 1 Taktzyklus pro Zeichen?
So in etwa. Aber 99,9% aller Programme stört das nicht, denn die CPU ist
in den meisten Fällen mit anderen Sachen beschäftigt als zu 100% Strings
zu kopieren.
Falk B. schrieb:> Nein, denn die Variable ist statisch, sie ist schon dauerhaft angelegt,> siehe oben.
Er meint die Ziel Variable, in die er den String hinein kopiert. Die ist
nicht statisch. Aber auch nicht auf dem Heap, sondern auf dem Stack.
Stefan ⛄ F. schrieb:> Er meint die Ziel Variable, in die er den String hinein kopiert. Die ist> nicht statisch. Aber auch nicht auf dem Heap, sondern auf dem Stack.
Die gibt es im 1. Beispiel aber nicht!
Frank M. schrieb:> Doch:char command[45] = "Dies ist ein zu modifizierendes Textgerippe\n";>> command ist nicht statisch im ersten Programm, deshalb wird dieses Array> bei jedem Aufruf von doSomething() neu gefüllt.
Er sagt hier ja explizit das er "command" auch verändern können möchte
und somit muss das im RAM stehen. Damit erstmal den ganzen Code zu
durchsuchen das der Programmierer das hier eventuell falsch deklariert
hat und er es eigentlich garnicht verändern will, soviel Esoterik ist
nicht der Stiel von C/C++. Das geht öfters davon aus das der
Programmierer weis was er will (und das auch richtig angeben kann:-).
folgender wäre zulässig:
1
charcommand[45]="Dies ist ein zu modifizierendes Textgerippe\n";
2
strcpy(command,"Neuer Text");
Hier würde das ganze meckern:
1
constcharcommand[]="Dies ist ein zu modifizierendes Textgerippe\n";
2
strcpy(command,"Neuer Text");
Durch das const sagt der Programiere das die Variable in ihrem
Gültigkeitsbereich nicht verändert werden darf und somit darf der
Compiler/Linker das auch annehmen und entsprechende Optimierungen
durchführen.
... mich interessiert mal der Sinn von static hier, wenn die Variable
doch sowieso "statisch" fix im Flash liegt.
Das erscheint mir reduntant/unnötig?! Sehe ich aber immer wiedermal ...
Ardur schrieb:> const static char Cmd[] PROGMEM = "Dies ist ein zu modifizierendes> Textgerippe\n";
Peter D. schrieb:> Ardur schrieb:>> Nun die RAM-sparende Variante:>> Spart das wirklich was?
Es spart die (unsichtbare) RAM-Kopie! Denn der String wird beim
Funktionsaufruf NICHT direkt aus dem Flash initialisiert sondern aus der
Kopie im RAM. Eben darum sind konstante Strings auf dem AVR Mist
(RAM-Verschwendung), welche man besser mit den _P Funktionen sowie
PSTR("") Makro umgeht. Das sollte DIR aber als altem AVR-Hasen aber
bekannt sein.
Dann ist also für meinen Fall, einen längeren String in einer Funktion
verändern zu müssen, die von Falk B. (falk) vorgeschlagene 3. Variante
die RAM- und Flash-sparendste:
1
voiddoSomething(charfall){
2
charcommand[45];
3
strcpy_P(command,PSTR("Dies ist ein zu modifizierendes Textgerippe\n"));
4
5
// verändere jetzt etwas am String command, bevor er weiterverarbeitet wird
Wenn du den modifizerten Text irgendwo seriell ausgibts (z.B. auf einem
Display) könntest du ihn auch in mehreren Stücke zerlegen. Beispiel:
Statt: "Die Temperatur ist # Grad Celsius", wo dann # durch die Zahl
ersetzt wird, könntest zu auch das tun:
ausgeben_P(PSTR("Die Temperatur ist "));
ausgeben(zahl);
ausgeben_P(PSTR(" Grad Celsius"));
Das würde noch viel weniger RAM belegen.
Apollo M. schrieb:> mich interessiert mal der Sinn von static hier, wenn die Variable doch> sowieso "statisch" fix im Flash liegt
Wäre sie nicht static, könnte sie nicht im flash liegen, daher ist
static nötig.
Falk B. schrieb:> Das sollte DIR aber als altem AVR-Hasen aber bekannt sein.
Das schlimme ist, dass es dem GCC Compiler nicht bekannt ist. Der könnte
optimieren, er weiss ob in Speicher hineingeschrieben wird, welche Kopie
unnötig ist, der könnte selber eine vernünftige Speicheraufteilung
erzeugen, aber er ist offenkundig noch immer zu doof dazu.
DAS ist das eigentlich Erschreckende im Jahr 2020.
Die ganzen Jubelperser "der beste Compiler, optimiert super, besser ist
man per Hand in Assembler nie" müsstest du ansprechen.
(ich musste gerade den Takt eines AVR von 1 auf 8 MHz hochsetzen, damit
er eine simple Interruptroutine noch schafft, die nicht häufiger als
alle 50us aufgerufen wird:)
MaWin schrieb:> Das schlimme ist, dass es dem GCC Compiler nicht bekannt ist. Der könnte> optimieren, er weiss ob in Speicher hineingeschrieben wird, welche Kopie> unnötig ist, der könnte selber eine vernünftige Speicheraufteilung> erzeugen, aber er ist offenkundig noch immer zu doof dazu.
Der arm-gcc kann diesbezüglich einiges besser, als der avr-gcc.
Vermutlich hat es damit zu tun, dass AVR spezielle Befehle für den
Zugriff auf den Flash Speicher brauchen, die gängigen ARM Controller
aber nicht.
Stefan ⛄ F. schrieb:> Statt: "Die Temperatur ist # Grad Celsius", wo dann # durch die Zahl> ersetzt wird, könntest zu auch das tun:>> ausgeben_P(PSTR("Die Temperatur ist "));> ausgeben(zahl);> ausgeben_P(PSTR(" Grad Celsius"));
1
#include<Streaming.h>
2
3
voidsetup()
4
{
5
Serial.begin(9600);
6
Serial<<F("Start: ")<<__FILE__<<endl;
7
}
8
9
voidloop()
10
{
11
floatgrad{47.11};
12
Serial<<F("Die Temperatur ist ")<<grad<<F(" Grad Celsius")<<endl;
MaWin schrieb:> Das schlimme ist, dass es dem GCC Compiler nicht bekannt ist. Der könnte> optimieren, er weiss ob in Speicher hineingeschrieben wird, welche Kopie> unnötig ist, der könnte selber eine vernünftige Speicheraufteilung> erzeugen, aber er ist offenkundig noch immer zu doof dazu.
Da das ja deiner Meinung nach so einfach zu implementieren ist: Na dann
mal ran, MaWin. Muss ja ein Klacks für jemanden wie dich sein. ;)
MaWin schrieb:> Der Compiler ist damit überfordert.
Kein Compiler dieser Welt ist foolproof. Wenn sich da der Anwender sich
absichtlich dummer darstellt, als er ist, kann der Compiler nichts
dafür.
Oliver
Arduino Fanboy D. schrieb:> Arduino Beispiel
Ja, so macht man das "richtig" im Arduino Style. Hat den Vorteil, dass
es genau so auch auf ARM Controllern funktioniert.