Normalerweise habe ich kein Problem damit, zu entscheiden, ob einige Daten global, statisch oder auf dem Stack sein müssen (hier keine dynamische Zuordnung, daher keine Verwendung des Heapspeichers). Ich habe auch ein paar Fragen / Antworten wie diese gelesen, aber meine Frage ist spezifischer, da sie im Vergleich zum Systemspeicher eine große Datenmenge enthält.
Ich arbeite an einem vorhandenen Code, den ich zu verbessern versuche (Design, mögliche Probleme, Leistung usw.). Dieser Code läuft auf einer alten 8-Bit-MCU mit nur 4 KB RAM . In diesem Code stelle ich die Verwendung eines Arrays von fast 1 KB (ja, 1 KB auf einem 4-KB-RAM- System). Jedes Byte dieses Arrays wird verwendet, das ist nicht die Frage. Das Problem ist, dass dieses Array ein statisches Array in der Datei ist, in der es deklariert ist, sodass sein Lebenszyklus mit dem des Programms identisch ist (dh als unendlich angesehen werden kann).
Nachdem ich den Code gelesen hatte, stellte ich jedoch fest, dass dieses Array keinen unendlichen Lebenszyklus benötigt, sondern vollständig prozedural aufgebaut und behandelt wird. Daher sollten wir es nur in der Funktion deklarieren können, in der es verwendet wird. auf diese Weise wäre es auf dem Stapel, und wir würden daher diese 1 KB RAM sparen.
Nun die Frage: Wäre das eine gute Idee? Aus Sicht des Designs gehört es zum Stack, wenn es keinen unendlichen / globalen Lebenszyklus benötigt. Aber hey, das ist 1 KB von 4 KB, gibt es keinen Nachteil , wenn 25% des Arbeitsspeichers so zugewiesen werden ? (das könnten 50% oder mehr des Stapels sein)
Könnte jemand Erfahrungen mit dieser Art von Situation teilen, oder könnte jemand über einen gültigen Grund nachdenken, dieses Array nicht auf den Stapel zu legen? Ich suche technische Nachteile sowie Kommentare zum Design.
Mir ist nur bewusst, dass ich beim Aufrufen dieser Funktion sicherstellen muss, dass tatsächlich 1 KB Stack frei sind. Vielleicht ist das alles, worauf ich aufpassen muss, vielleicht auch nicht.
Antworten:
Ja, und das ist eine starke Einschränkung. Sie sind statisch sicherer, als einen so großen verfügbaren Speicherplatz auf dem Stapel zu haben. Wenn der Code klein ist und Sie ein aktuelles GCC zum Kompilieren Ihres Codes verwenden, lesen Sie dies .
Übrigens könnten einige billige Mikroprozessoren einen "großen" Anrufrahmen teurer als einen "normalen" verwenden (z. B. weil ihr Befehlssatz einen 1-Byte-Versatz vom Stapelzeiger begünstigen würde). YMMV.
Wenn Sie in C codieren und der Meinung sind, dass der Speicherplatz Ihres großen Arrays für andere Zwecke wiederverwendet werden kann, sollten Sie ihn möglicherweise zu einem Gewerkschaftsmitglied (mit einer globalen Variablen vom
union
Typ) machen. Ja, das ist ziemlich hässlich.Alternativ können Sie einen für Ihre Anwendung geeigneten primitiven Heap-Allokator (und eine andere API als
malloc
&free
....) codieren .quelle
gcc -flto -Os
? ) und Sie könnten etwas Gedächtnis gewinnen ....Bei einem großen Stack ist man eher vorsichtig, da er im RAM rückwärts wächst und Variablenwerte überschreibt, was zu unerklärlichem Verhalten führt. Es wird noch schlimmer, weil Sie die niedrigstmögliche Stapelzeigeradresse kennen und die zuzuweisende Größe subtrahieren müssen, wenn Sie in die Routine eintreten.
Dies ist alles eine Aufgabe für die Hardwarespeicherverwaltung (sollte Traps oder Fehler erzeugen, wenn ein Stapelüberlauf auftritt) oder für den Compiler, da dieser über Funktionen für diese Art der Analyse verfügt.
Ansonsten können Sie mit Ihrem RAM machen, was Sie wollen.
quelle
Wie bereits in früheren Antworten erwähnt, würde ich auch empfehlen, das Array zunächst statisch zu lassen, wenn es in den Arbeitsspeicher passt. In den meisten Fällen ist es sehr viel wichtiger, einen deterministischen Speicherbedarf zu haben, selbst wenn dies bedeutet, dass Sie Speicher für Variablen "verschwenden", die nicht ständig verwendet werden. Wenn Sie große Arrays auf Ihren Stapel legen, wird er viel zu schnell ausgeblasen, und Stapelüberläufe können zu schwer zu findenden und schwer zu reproduzierenden Problemen führen (wenn Sie den Stapel nicht mit MMU schützen können).
Der Vorschlag, den Block mit einigen anderen Daten mit union zu teilen, ist IMO-gültig. Er kann jedoch auch zu schwer zu findenden Problemen führen, wenn Sie darin falsche Variablen finden.
Wenn Ihnen der Speicher ausgeht und Sie dringend kürzer lebende Variablen erstellen müssen, um ihn gemeinsam zu nutzen, bevor Sie das Array in den Stapel verschieben, würde ich eine dynamische Speicherzuweisung in Betracht ziehen, auch wenn dies seine eigenen Nachteile hat. In diesem Fall ist dies möglicherweise keine Antwort, da das Array im Vergleich zum verfügbaren Speicher ziemlich groß klingt.
quelle
Sie haben eine weitere Option, wenn Sie über einen Flash-Speicher verfügen. Sie können die Zugriffsgeschwindigkeit für den RAM ausgleichen, indem Sie Ihre Daten in Flash speichern und dort lesen und suchen. Sie müssten jeweils nur einen Datensatz in den RAM laden. Es wird etwas komplizierter, wenn Sie die Datensätze aktualisieren müssen. Sie benötigen einen segmentierten, verschleißarmen Mechanismus. Ich habe dies in der Vergangenheit getan und einen Index eingefügt, um den Zugriff zu beschleunigen.
quelle
Insbesondere bei der Arbeit mit eingebetteten Systemen möchten Sie, dass möglichst viele Fehler während der Kompilierung auftreten und zur Laufzeit nichts ausfällt (schön, wenn wir dies erreichen könnten ...).
Wenn Sie große Arrays erstellen, die Sie möglicherweise in einem beliebigen Status Ihres Programms benötigen, der statisch zugewiesen ist, ist dies genau das. Der Linker warnt Sie schließlich, dass dies nicht in den Arbeitsspeicher passt überläuft.
quelle