Hallo Forum,
ich habe in einem Programm (Rust, Linux) mehrere grosse Arrays, zusammen
10MB. Damit das aber funktioniert, muesste ich beim compilieren die
Stacksize aendern.
1
thread 'main' has overflowed its stack
2
fatal runtime error: stack overflow
Ich kann statt den Arrays auch Vectoren nehmen, die bieten mir aber
ansonsten (fuer meinen Anwendungsfall) keine Vor-, aber auch keine
Nachteile.
Ist das aender der Stacksize beim compilieren "gute" praxis?
Oder ist das eher ein "Kann man machen, aber..."?
Gruesse
Hallo,
zwei kurze Gegenfragen:
1. Ist das der Compiler, der sich mit einem Stackoverflow verabschiedet?
Dann wäre ein Issue auf Github hilfreich. Falls es ein Stack Overflow
zur Laufzeit deines Programms ist:
2. Wie sind die Arrays angelegt worden?
Der folgende Weg via Box::new() "geht nicht" für große Arrays:
1
let _ = Box::new([0_u8; 4*1024*1024]); // 4 MB
Er funktioniert deswegen nicht, weil das Array erst auf dem Stack
angelegt wird (im Beispiel `[0_u8; 4*1024*1024]`) und dann auf den Heap
verschoben wird. Korrekt wäre es tatsächlich einen Vektor zu nehmen, da
dieser direkt auf dem Heap alloziert. Wenn du aber eine "owned
slice"/"boxed slice" haben möchtest, dann geht das so:
1
let _ = vec![0_u8; 4*1024*1024].into_boxed_slice();
Siehe dazu auch https://github.com/rust-lang/rust-clippy/issues/4520.
Das als mögliche Verhinderung des Stack-Overflows.
Deine Eigentliche Frage bezog sich aber auf das Ändern der Stack-Größe.
Das kann man meiner Meinung nach zwar machen, weißt aber im Allgemeinen
auf einen Fehler hin. Per default hast du glaube ich 2 MB Stack pro
Thread; das sollte reichen, sofern man große Dinge, wie deine Arrays,
auf den Heap legt.
rst schrieb:> 1. Ist das der Compiler, der sich mit einem Stackoverflow verabschiedet?
Nein, das ist das Programm.
rst schrieb:> 2. Wie sind die Arrays angelegt worden?
Wenn diese Variablen für die ganze Programmlaufzeit gebraucht werden,
können sie auf den Stack. Werden sie nur kurtzzeitig gebraucht ist es
unschön dafür grundsätzlich Speicher auf dem Stack zu reservieren.
Hallo,
prinzipiell hat intus recht, auf dem Stack sind kurzlebige kleine
Datenstrukturen schöner als auf dem Heap. Aber der Stack ist nicht für
riesige Datenstrukturen da, daher ja auch der Stack Overflow.
Eine saubere Lösung wäre es, die großen Strukturen auf den Heap
auszulagern:
Weg mit dem Troll ! Aber subito schrieb:> Weshalb sowas dynamisch auf Stack/Heap allozieren, und nicht> global ?
Global ist stack - weil das array zugross ist fuer den default stack,
auch unter C
cppbert3 schrieb:> Weg mit dem Troll ! Aber subito schrieb:> Weshalb sowas dynamisch auf Stack/Heap allozieren, und nicht global ?>> Global ist stack - weil das array zugross ist fuer den default stack,> auch unter C
Global ist nicht Stack! Sondern Global. Wenn deine Routinen, die das
sonst auf dem Stack gemacht haben, nicht reentrent sein müssen, wäre
das, oder ein davorschreiben von 'static', wahrscheinlich die beste
Variante.
Globale Variablen werden beim Laden des executable alloziert Und sind
so lange gültig, wie das Programm läuft.
Werden die globalen Variablen mit null oder gar nicht initialisiert,
dann nehmen sie im Binary Noch nicht Mal Platz weg, sondern werden und
bss angeht gepackt, anstelle des Data Segment
DPA schrieb:> Wurde rust nicht extra so designt, um genau solche> Speicherzugriffsverletzungen zu verhindern?
Tut es doch: anstatt den Stack still zu überschreiben bricht das
Programm kontrolliert ab. Und das nicht mit einem Segfault oder einem
Abort, sondern mit einem Sprachkonstrukt (einer sog. panic). Was soll
die Sprache denn tun, wenn der Programmierer mehr Speicher als verfügbar
auf dem Stack alloziert? Die einzige mehr oder weniger triviale Lösung
wäre es, alles auf dem Heap zu allozieren (vgl. Java), aber das geht zu
lasten der Performance.
Das vorliegende Problem ist mehr oder weniger sprachunabhängig.
Kurz zum Thema global: globale Variablen sind in anderen Sprachen
schlechter Stil, ebenso in Rust. Es gibt seltene Ausnahmen, aber
Normalerweise ist es schöner, Ressourcen (nichts anderes ist
Stack-/Heap-Speicher) erst wenn benötigt zu belegen und frühst möglich
freizugeben. In Rust könnte man wenn man möchte genauso statics anlegen,
jedoch sind diese aufwändiger zu handhaben (statics sind nicht
threadsafe, also benötigt man einen Mutex oder ähnliches, was einen
Performance-Nachteil mit sich bringt, oder man nutzt unsafe-Blöcke). Die
Sprache "zwingt" den Programmierer also sogar vorsichtig in die
"richtige" Richtung, ohne den anderen Weg aber zu versperren.
Imho ist es nicht ratsam, auf Teufel komm raus auf den Heap zu
verzichten. Gerade für dynamische oder große statische Datenstrukturen
ist er auf einem PC unerlässlich (auf Mikrocontrollern ggf. eine andere
Sache).
Grüße
Danke fuer die Antworten. :)
Das Objekt existiert fuer die gesamte Laufzeit des Programms.
Hat die Box
1
x: Box<[u8]>
Vorteile gegenueber eines Vectors?
1
x: Vec<u8>
DPA schrieb:> Wurde rust nicht extra so designt, um genau solche> Speicherzugriffsverletzungen zu verhindern?
Das Programm stuertzt direkt nach dem Start ab, es wird "kein Code"
ausgefuehrt (z.B. println!(...)), egal ob der Code vor oder hinter der
Allokation steht.
Schoen waere es, wenn der Compiler eine Warnung bzgl. der Stackgroesse
geben wuerde. Die Groesse der Arrays steht ja zur Compiletime fest. Aber
gut, ich hatte gar nicht auf dem Schirm, das es da eine
Default-Stacksize gibt. Wieder was gelernt. :)
rst schrieb:> Imho ist es nicht ratsam, auf Teufel komm raus auf den Heap zu> verzichten.
War auch keine "Absicht". Learning by doing. :)
Gruesse
-------------------
Edit:
Habe gerade gesehen, das im Bugtracker ueber eine Warnung bzgl. der
Stackgroesse/grossen Arrays disskutiert wird.
Kaj G. schrieb:> Ist das aender der Stacksize beim compilieren "gute" praxis
Kommt auf die Lebensdauer der Arrays an.
Wenn du den Speicherplatz des Arrays genau so lange brauchst wie die
Funktion dauert, kommen Daten auf den Stack.
Dessen Speicherverwaltung ist schneller.
Bei nur ein paar Arrays wäre der Verwaltungsoverhead aber
vernachlässigbar.
Da wäre erstens die Frage:
Möchte man auch auf anderen Plattformen kompilieren ohne dort erstmal
rausfinden zu müssen wie man die Stacksize ändert ?
Möchte man den Code in eine DLL tun die den Stack vom Hauptprogramm
benutzt, der nicht unter deinem Einfluss steht.
Müssen 10 MB überhaupt in ddn Speicher, oder nutzt man memory mapping
von files.
Braucht dein Programm immer 10 MB oder auch mal nur 1 MB oder 100k ?
Dann könnte es in den kleinen Fällen mit weniger Gesamtspeicher auf
kleineren Rechnern laufen wenn du es per heap machst.
Kaj G. schrieb:> Aber gut, ich hatte gar nicht auf dem Schirm, das es da eine> Default-Stacksize gibt. Wieder was gelernt. :)
Ich hab das bei einem etwas größeren rekursiven Algorithmus gelernt, der
machte bei 8 MB oder so "puff". Eigentlich ist das auch logisch, da ja
alle Threads (mit jeweils ihrem eigenen Stack) sich den gleichen
Adressraum teilen müssen, kann der Stack nicht beliebig groß sein - bei
32 Bit ist das relevant.
rst schrieb:> Und das nicht mit einem Segfault oder einem> Abort, sondern mit einem Sprachkonstrukt (einer sog. panic).
Ob mir die Sprache ein "panic", ein abort() oder eine
OutOfMemoryException schenkt, ist aus meiner Sicht jetzt kein großer
Unterschied. Das sind alles Suizidfragmente, auf die man als
Programmierer nur sehr eingeschränkt reagieren kann.
Ein Segfault ist eine etwas andere Hausnummer, zugegeben. Aber auch den
kann man im Prinzip ganz normal behandeln.