C / C ++ maximale Stapelgröße des Programms

115

Ich möchte DFS auf einem 100 x 100-Array ausführen. (Angenommen, Elemente des Arrays stellen Diagrammknoten dar.) Unter der Annahme des schlimmsten Falls kann die Tiefe rekursiver Funktionsaufrufe bis zu 10000 betragen, wobei jeder Aufruf bis zu 20 Bytes benötigt. Ist es also machbar, dass die Möglichkeit eines Stapelüberlaufs besteht?

Was ist die maximale Größe des Stapels in C / C ++?

Bitte geben Sie für gcc für beide
1) cygwin unter Windows
2) Unix an

Was sind die allgemeinen Grenzen?

avd
quelle
11
Sie wissen, dass Sie die Tiefensuche ohne Rekursion implementieren können, oder?
Sebastian
4
Nein, ich weiß es nicht, bitte erkläre es.
avd
1
Ich habe in meiner Antwort ein kleines Beispiel für DFS ohne Rekursion gemacht
Andreas Brinck
56
plus eins für eine Frage zum tatsächlichen Stapelüberlauf
Sam Watkins
3
@SamWatkins Ja, eines der größten Probleme mit dem Namen Stack Overflow ist für mich, dass ich möglicherweise bei Google nach "Stack Overflow" suche und auf dieser Website lande, aber nicht unbedingt / weniger wahrscheinlich bei Fragen zu Stack Overflows ...
lalilulelost

Antworten:

106

In Visual Studio beträgt die Standardstapelgröße meiner Meinung nach 1 MB. Bei einer Rekursionstiefe von 10.000 kann jeder Stapelrahmen höchstens ~ 100 Byte betragen, was für einen DFS-Algorithmus ausreichend sein sollte.

Bei den meisten Compilern, einschließlich Visual Studio, können Sie die Stapelgröße angeben. Bei einigen (allen?) Linux-Varianten ist die Stapelgröße nicht Teil der ausführbaren Datei, sondern eine Umgebungsvariable im Betriebssystem. Sie können dann die Stapelgröße mit überprüfen ulimit -sund sie beispielsweise mit auf einen neuen Wert setzen ulimit -s 16384.

Hier ist ein Link mit Standardstapelgrößen für gcc.

DFS ohne Rekursion:

std::stack<Node> dfs;
dfs.push(start);
do {
    Node top = dfs.top();
    if (top is what we are looking for) {
       break;
    }
    dfs.pop();
    for (outgoing nodes from top) {
        dfs.push(outgoing node);
    }
} while (!dfs.empty())
Andreas Brinck
quelle
12
Und nur als Referenz ist ein BFS dasselbe, außer dass Sie ein FIFO anstelle eines Stapels verwenden.
Steve Jessop
Ja, oder in STL-Jargon verwenden Sie eine std :: deque mit pop_front / push_back
Andreas Brinck
Ihre DFS mit Stapelergebnissen unterscheidet sich von der Rekursionsversion. In einigen Fällen spielt es keine Rolle, aber in anderen (zum Beispiel in topologischer
Hinsicht
Ja, das Standardlimit für VS beträgt tatsächlich 1 MB. Weitere Informationen und die Möglichkeit, einen anderen Wert festzulegen
FrankS101
Ich bevorzuge die Verwendung einer expliziten Stapeldatenstruktur für solche Algorithmen anstelle einer Rekursion, damit 1. nicht von der Größe des Systemstapels abhängt, 2. der Algorithmus geändert werden kann, um eine andere Datenstruktur zu verwenden, z. B. Warteschlange oder Prioritätswarteschlange, ohne zu werfen den ganzen Code aus.
Sam Watkins
47

Stapel für Fäden sind oft kleiner. Sie können die Standardeinstellung zur Verbindungszeit oder auch zur Laufzeit ändern. Als Referenz sind einige Standardeinstellungen:

  • glibc i386, x86_64 7,4 MB
  • Tru64 5.1 5.2 MB
  • Cygwin 1,8 MB
  • Solaris 7..10 1 MB
  • MacOS X 10.5 460 KB
  • AIX 5 98 KB
  • OpenBSD 4.0 64 KB
  • HP-UX 11 16 KB
Pixelbeat
quelle
14
Empirisch bestimmt von Bruno Haible lists.gnu.org/archive/html/bug-coreutils/2009-10/msg00262.html
Pixelbeat
17

Plattformabhängig, Toolchain-abhängig, ulimit-abhängig, parameterabhängig ... Es ist überhaupt nicht spezifiziert, und es gibt viele statische und dynamische Eigenschaften, die es beeinflussen können.

DrPizza
quelle
4
Es gibt keine "allgemeinen Grenzen". Unter Windows mit den Standardoptionen für VC ++ - Linker und dem Standardverhalten von CreateThread, normalerweise etwa 1 MiB pro Thread. Unter Linux mit einem unbegrenzten Benutzer gibt es meiner Meinung nach normalerweise keine Begrenzung (der Stapel kann einfach nach unten wachsen, um fast den gesamten Adressraum zu belegen). Wenn Sie fragen müssen, sollten Sie den Stapel grundsätzlich nicht verwenden.
DrPizza
1
Auf eingebetteten Systemen haben Sie möglicherweise 4 KB oder weniger. In diesem Fall müssen Sie auch dann fragen, wenn es sinnvoll ist, den Stapel zu verwenden. Die Antwort ist normalerweise ein gallisches Achselzucken.
Steve Jessop
1
Ah wahr, auch oft im Kernel-Modus.
DrPizza
6

Ja, es besteht die Möglichkeit eines Stapelüberlaufs. Der C- und C ++ - Standard schreibt keine Dinge wie die Stapeltiefe vor, diese sind im Allgemeinen ein Umweltproblem.

In den meisten anständigen Entwicklungsumgebungen und / oder Betriebssystemen können Sie die Stapelgröße eines Prozesses entweder zum Zeitpunkt der Verknüpfung oder zum Laden anpassen.

Sie sollten angeben, welches Betriebssystem und welche Entwicklungsumgebung Sie für eine gezieltere Unterstützung verwenden.

Unter Ubuntu Karmic Koala ist der Standardwert für gcc beispielsweise 2M reserviert und 4K festgeschrieben. Dies kann jedoch geändert werden, wenn Sie das Programm verknüpfen. Verwenden Sie dazu die --stackOption ld.

paxdiablo
quelle
2
@lex: Es gibt keine allgemeinen Grenzen. Es hängt von vielen Parametern ab.
Michael Foukarakis
@paxdiablo: WAS bedeutet reserviert und festgeschrieben?
avd
2
Reserviert ist, wie viel Adressraum zugewiesen werden soll, festgeschrieben, wie viel Sicherungsspeicher angehängt werden soll. Mit anderen Worten, das Reservieren des Adressraums bedeutet nicht, dass der Speicher vorhanden ist, wenn Sie ihn benötigen. Wenn Sie nie mehr als 4K-Stapel verwenden, verschwenden Sie keinen echten Speicher für die anderen 1,6 MB. Wenn Sie sicherstellen möchten, dass genügend Stapel vorhanden sind, sollten reserviert und festgeschrieben identisch sein.
paxdiablo
2
@paxdiablo 2M - 4k ist nicht 1.6M. Nur sagen. (hat mich die ersten 3 Male verwirrt, als ich Ihren Kommentar gelesen habe)
Griffin
2
@griffin, ein großes Lob für die erste Person, die das seit mehr als 3 Jahren fängt. Ich meinte natürlich "Rest" - ich werde eine tatsächliche Zahl vermeiden, um keinen weiteren möglichen Fehler zu machen :-)
paxdiablo
5

Bei der Arbeit ging mir gerade der Stapel aus, es war eine Datenbank und es wurden einige Threads ausgeführt. Im Grunde hatte der vorherige Entwickler ein großes Array auf den Stapel geworfen, und der Stapel war sowieso niedrig. Die Software wurde mit Microsoft Visual Studio 2015 kompiliert.

Obwohl der Thread keinen Stapel mehr hatte, schlug er stillschweigend fehl und fuhr fort. Der Stapel lief nur über, wenn er auf den Inhalt der Daten auf dem Stapel zugreifen wollte.

Der beste Rat, den ich geben kann, ist, keine Arrays auf dem Stapel zu deklarieren - insbesondere in komplexen Anwendungen und insbesondere in Threads -, sondern Heap zu verwenden. Dafür ist es da;)

Denken Sie auch daran, dass es beim Deklarieren des Stapels möglicherweise nicht sofort fehlschlägt, sondern nur beim Zugriff. Ich vermute, dass der Compiler den Stapel unter Windows "optimistisch" deklariert, dh er geht davon aus, dass der Stapel deklariert wurde und eine ausreichende Größe hat, bis er verwendet wird, und stellt dann fest, dass der Stapel nicht vorhanden ist.

Unterschiedliche Betriebssysteme haben möglicherweise unterschiedliche Richtlinien für die Stapeldeklaration. Bitte hinterlassen Sie einen Kommentar, wenn Sie diese Richtlinien kennen.

Eule
quelle
3

Ich bin mir nicht sicher, was Sie unter einer Tiefensuche in einem rechteckigen Array verstehen, aber ich gehe davon aus, dass Sie wissen, was Sie tun.

Wenn das Stapellimit ein Problem darstellt, sollten Sie in der Lage sein, Ihre rekursive Lösung in eine iterative Lösung umzuwandeln, die Zwischenwerte auf einen Stapel überträgt, der vom Heap zugewiesen wird.

Dave Kirby
quelle