resource.setrlimit
muss auch verwendet werden, um die Stackgröße zu erhöhen und Segfault zu verhindern
Der Linux-Kernel beschränkt den Stapel von Prozessen.
Python speichert lokale Variablen auf dem Stapel des Interpreters, und so nimmt die Rekursion den Stapelspeicher des Interpreters ein.
Wenn der Python-Interpreter versucht, das Stapellimit zu überschreiten, bewirkt der Linux-Kernel einen Segmentationsfehler.
Die Stapellimitgröße wird mit den Systemaufrufen getrlimit
und setrlimit
kontrolliert.
Python bietet über das Modul resource
Zugriff auf diese Systemaufrufe.
sys.setrecursionlimit
erwähnt z.B. unter https://stackoverflow.com/a/3323013/895245 erhöht nur das Limit, das der Python-Interpreter selbst auf seine eigene Stapelgröße auferlegt, berührt aber nicht das Limit, das der Linux-Kernel dem Python-Prozess auferlegt.
Beispielsprogramm:
main.py
import resource
import sys
print resource.getrlimit(resource.RLIMIT_STACK)
print sys.getrecursionlimit()
print
# Würde ohne diese Zeile Segfault verursachen.
resource.setrlimit(resource.RLIMIT_STACK, [0x10000000, resource.RLIM_INFINITY])
sys.setrecursionlimit(0x100000)
def f(i):
print i
sys.stdout.flush()
f(i + 1)
f(0)
Natürlich, wenn Sie weiterhin setrlimit
erhöhen, wird Ihr RAM irgendwann ausgehen, was entweder Ihren Computer aufgrund des Austausch-Irrsinns zum Stillstand bringen oder Python über den OOM-Killer beenden wird.
Von der Bash aus können Sie das Stapellimit (in kb) sehen und setzen mit:
ulimit -s
ulimit -s 10000
Der Standardwert für mich beträgt 8 MB.
Siehe auch:
Getestet auf Ubuntu 16.10, Python 2.7.12.
4 Stimmen
Siehe auch stackoverflow.com/questions/5061582/…
21 Stimmen
Memoisierung könnte die Geschwindigkeit Ihrer Funktion steigern und ihre effektive rekursive Tiefe erhöhen, indem zuvor berechnete Werte beendet werden, anstatt die Stapelgröße zu erhöhen.
6 Stimmen
Die Rekursionsobergrenze beträgt in der Regel 1000.
1 Stimmen
@Boris Warum bleibt es dann bei
997
stecken? Liegt es daran, dass der Python-Interpreter die ersten 3 Ebenen des Stapels einnimmt?6 Stimmen
@tonix der Interpreter fügt einen Stapelrahmen hinzu (die
Zeile , in
in Stapelverfolgungen) und dieser Code nimmt 2 Stapelrahmen fürn=1
(weil der Basisfalln < 1
ist, also fürn=1
rekursiert es trotzdem). Und ich nehme an, dass das Rekursionslimit nicht inklusiv ist, wie in "Fehler, wenn Sie 1000 erreichen" und nicht "Fehler, wenn Sie 1000 überschreiten (1001)".997 + 2
ist weniger als 1000, also funktioniert es,998 + 2
nicht, weil es das Limit erreicht.0 Stimmen
@Boris Also sind
997
Stapelrahmen vonrecursive_function
belegt und die letzten2
sind vom Interpreter selbst besetzt (eingebaute Zuweisung)? Habe ich das richtig verstanden?6 Stimmen
@tonix nein.
recursive_function(997)
funktioniert, bricht bei998
ab. Wenn Sierecursive_function(998)
aufrufen, werden 999 Stapelrahmen verwendet und 1 Rahmen wird vom Interpreter hinzugefügt (weil Ihr Code immer so ausgeführt wird, als ob er Teil des obersten Moduls wäre), was dazu führt, dass er das 1000er-Limit erreicht.0 Stimmen
Der Weg, wie diese Funktion geschrieben ist, führt dazu, dass es
n+1
Frames dauert, um das Ergebnis fürn
zu berechnen.0 Stimmen
@tonix, ich habe deine Frage hier gestellt stackoverflow.com/questions/59347491/…
0 Stimmen
@Boris Ich habe noch einmal nachgeschaut und in meinem Fall funktioniert
recursive_function(995, 0)
mitRekursionsgrenze = 1000
, währendrecursive_function(996, 0)
nicht funktioniert... Also denke ich, dass zusätzliche Stapelrahmen verwendet werden (Ich benutze Python 3.6).0 Stimmen
@tonix Ich habe es gerade auf Python 3.6.9 durch die REPL ausprobiert und erhalte immer noch das gleiche Verhalten, 997 funktioniert, 998 nicht. Wie führst du diese Funktion aus? Du musst es auf eine Weise machen, die 2 Stapelrahmen hinzufügt. Führst du es aus einer
main()
-Funktion oder importierst du eine Datei, in der du die Funktion definierst? Wenn du es mit IPython ausführst, wäre es nicht überraschend, wenn das auch ein paar Rahmen hinzufügt.0 Stimmen
Ich verwende direkt die Python-Befehlszeilenschnittstelle. Ich starte
python
und schreibe dann den Code auf der Eingabeaufforderung des Interpreters>>>
und führe ihn aus.0 Stimmen
@tonix verwendest du Python 2 oder 3. Wenn du nur "
python
" eingibst und nicht "python3
", ist es wahrscheinlich 2.0 Stimmen
@Boris Ich kann bestätigen, dass ich Python 3 verwende, weil ich diese Ausgabe sehe, wenn ich im CLI
python
eingebe:Python 3.6.5 (Standard, 30. März 2018, 06:42:10) [GCC 4.2.1 kompatibel Apple LLVM 9.0.0 (clang-900.0.39.2)] auf Darwin Geben Sie "help", "copyright", "credits" oder "license" für weitere Informationen ein. >>>