Comment augmenter la taille de la pile pour une application ruby. Obtenir une application récursive: Niveau de pile trop profond (SystemStackError)

StackOverflow https://stackoverflow.com/questions/242617

  •  04-07-2019
  •  | 
  •  

Question

Publier une question de débordement de pile sur stackoverflow.com, quel amusement: -)

J'utilise un code Ruby récursif et j'obtiens le: "Niveau de pile trop profond (SystemStackError)",

.

(Je suis à peu près sûr que le code fonctionne, que je ne suis pas dans une spirale de mort récursive infinie, mais ce n'est pas le problème de toute façon)

Est-il possible de modifier la profondeur / taille de pile autorisée pour mon application Ruby?

Je ne comprends pas bien s'il s'agit d'une restriction dans Ruby, car l'erreur dit "Niveau de pile", ce qui me donne l'impression que Ruby compte en quelque sorte les "niveaux" de pile, ou si cela signifie simplement que la pile est pleine.

J'ai essayé d'exécuter ce programme sous Vista et Ubuntu avec le même résultat. Sous Ubuntu, j’ai essayé de changer la taille de la pile avec 'ulimit -s' de 8192 à 16 000, mais cela n’a rien changé.

Modifier: Merci pour les commentaires.
Je me rends bien compte que l'utilisation d'une fonction récursive n'est peut-être pas la solution la plus robuste. Mais ce n'est pas le point non plus. Je me demande simplement s'il existe un moyen d'augmenter la taille de la pile .. période. Et comme je l’ai mentionné, j’ai essayé d’exécuter ulimit -s 16000 avant d’exécuter le script ruby ??.. sans aucune amélioration .. Est-ce que je l’utilise mal?

Edit2: J'avais en fait une récursion infinie dans un cas limite du code.
La trace de pile ruby ??tronquée lorsque vous obtenez l'erreur " Niveau de la pile trop profond " est un peu trompeuse.
Lorsque vous avez un comportement récursif impliquant plusieurs fonctions, vous avez l’impression que le nombre de récursions est bien inférieur à ce qu’il est réellement. Dans cet exemple, on peut penser qu’il se bloque après un peu plus de 190 appels, mais qu’il s’agit d’environ 15 000 appels

tst.rb:8:in `p': stack level too deep (SystemStackError)
        from tst.rb:8:in `bar'
        from tst.rb:12:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
         ... 190 levels...
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:22

-Andreas

Était-ce utile?

La solution

Ruby utilise la pile C, vos options incluent donc d’utiliser ulimit ou de le compiler avec un indicateur de taille de pile du compilateur / éditeur de liens. La récursion de la queue n'a pas encore été mise en œuvre et le support actuel de Ruby pour la récursion n'est pas si bon. Comme la récursion est élégante et élégante, vous pouvez envisager de faire face aux limitations du langage et d’écrire votre code différemment.

Autres conseils

Cette question et ses réponses semblent remonter à Ruby 1.8.x, qui utilisait la pile C. Ruby 1.9.x et versions ultérieures utilisent une machine virtuelle disposant de sa propre pile. Dans Ruby 2.0.0 et versions ultérieures, la taille de la pile de machines virtuelles peut être contrôlée via la variable d’environnement RUBY_THREAD_VM_STACK_SIZE .

Si vous êtes certain de ne pas avoir une situation de récursion infinie, votre algorithme n'est probablement pas approprié pour que Ruby l'exécute de manière récursive. Convertir un algorithme de récursion en différents types de pile est assez facile et je vous suggère de l'essayer. Voici comment vous pouvez le faire.

def recursive(params)
  if some_conditions(params)
     recursive(update_params(params))
  end
end

recursive(starting_params)

se transformera en

stack = [starting_params]
while !stack.empty?
  current_params = stack.delete_at(0)
  if some_conditions(current_params)
    stack << update_params(current_params)
  end
end

Yukihiro Matsumoto écrit ici

  

Ruby utilise la pile C, de sorte que vous devez   utilisez ulimit pour spécifier une limite   profondeur de la pile.

Pensez à ce qui se passe avec le code. Comme d'autres affiches l'ont mentionné, il est possible de pirater le code C de l'interprète. Toutefois. le résultat sera que vous utilisez plus de RAM et n’avez aucune garantie que vous ne ferez pas exploser la pile.

La meilleure solution serait de proposer un algorithme itératif pour ce que vous essayez de faire. Parfois, la mémorisation peut aider et parfois vous n’utilisez pas les éléments que vous poussez sur la pile, auquel cas vous pouvez remplacer les appels récursifs par un état mutable.

Si vous débutez dans ce genre de choses, consultez le SICP ici pour quelques idées. ...

Je viens d'avoir le même problème et il est très facile de résoudre le problème sous Linux ou Mac. Comme indiqué dans les autres réponses, Ruby utilise le paramètre de pile système. Vous pouvez facilement changer cela sur Mac et Linux en définissant la taille de la pile. Exemple Fox:

ulimit -s 20000

À partir de Ruby 1.9.2 , vous pouvez activer l'optimisation d'appel final avec quelque chose comme:

RubyVM::InstructionSequence.compile_option = {
  tailcall_optimization: true,
  trace_instruction: false
}

RubyVM::InstructionSequence.new(<<-EOF).eval
  def me_myself_and_i
    me_myself_and_i
  end
EOF
me_myself_and_i # Infinite loop, not stack overflow

Cela évitera l'erreur SystemStackError si l'appel récursif se trouve à la fin de la méthode et uniquement la méthode . Bien entendu, cet exemple donnera lieu à une boucle infinie. Il est probablement préférable de déboguer en utilisant une récursion peu profonde (et aucune optimisation) avant de passer à une récursion profonde.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top