Cómo aumentar el tamaño de pila para una aplicación de ruby. Aplicación recursiva obteniendo: nivel de pila demasiado profundo(SystemStackError)
recursion (7)
Publicando una pregunta de desbordamiento de pila en stackoverflow.com, qué divertido :-)
Estoy ejecutando un código de Ruby recursivo y obtengo el: "Stack level too deep (SystemStackError)"
(Estoy bastante seguro de que el código funciona, que no estoy en una espiral infinita de muerte recursiva, pero ese no es el punto de todos modos)
¿Hay alguna forma de cambiar la profundidad / tamaño de la pila permitida para mi aplicación Ruby?
No entiendo si esto es una restricción en Ruby, ya que el error dice "Nivel de pila", lo que me da la impresión de que Ruby de alguna manera cuenta los "niveles" de pila, o si simplemente significa que la pila está llena.
He intentado ejecutar este programa tanto en Vista como en Ubuntu con el mismo resultado. En Ubuntu intenté cambiar el tamaño de la pila con ''ulimit -s'' de 8192 a 16000, pero eso no cambió nada.
Editar: Gracias por los comentarios.
Me doy cuenta de que usar una función recursiva quizás no sea la manera más sólida de hacerlo. Pero ese tampoco es el punto. Simplemente me pregunto si hay una manera de aumentar el tamaño de la pila ... punto. Y como mencioné, intenté ejecutar ulimit -s 16000 antes de ejecutar el script de ruby ... sin ninguna mejora ... ¿Lo estoy usando mal?
Edit2: De hecho, tenía una recursión infinita en una caja de borde del código.
El seguimiento de la pila de ruby truncado cuando obtiene el error "Stack level too deep"
es un poco engañoso.
Cuando se tiene un comportamiento recursivo que implica varias funciones, se tiene la impresión de que el número de recursiones es mucho más bajo de lo que realmente es. En este ejemplo, uno podría pensar que se bloquea después de un poco más de 190 llamadas, pero en realidad se trata de alrededor de 15000 llamadas
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
A partir de Ruby 1.9.2 , puede activar la optimización de la cola de llamadas con algo como:
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
Eso evitará el error SystemStackError
si la llamada recursiva está al final del método y solo el método . Por supuesto, este ejemplo dará como resultado un bucle infinito. Probablemente sea mejor depurar utilizando recursividad superficial (y sin optimización) antes de recurrir a la recursión profunda.
Esta pregunta y sus respuestas parecen remontarse a Ruby 1.8.x, que usó la pila C. Ruby 1.9.xy posterior usan una máquina virtual que tiene su propia pila. En Ruby 2.0.0 y posterior, el tamaño de la pila de VM se puede controlar a través de la variable de entorno RUBY_THREAD_VM_STACK_SIZE
.
Piensa en lo que está pasando con el código. Como otros carteles han mencionado, es posible piratear el código C del intérprete. Sin embargo. el resultado será que estás usando más RAM y no tienes garantía de que no vuelvas a volar la pila.
La solución realmente buena sería crear un algoritmo iterativo para lo que estás tratando de hacer. A veces, la memorización puede ayudar y, a veces, uno descubre que no está utilizando las cosas que está presionando en la pila, en cuyo caso puede reemplazar las llamadas recursivas con estado mutable.
Si eres nuevo en este tipo de cosas, eche un vistazo al SICP here para obtener algunas ideas ...
Ruby usa la pila C para que tus opciones incluyan el uso de ulimit o la compilación de Ruby con algún indicador de tamaño de pila de compilador / enlazador. La recurrencia de la cola aún no se ha implementado y el soporte actual de Ruby para la recursión no es tan bueno. Como la recursión es genial y elegante, es posible que desee considerar hacer frente a las limitaciones del idioma y escribir su código de una manera diferente.
Si estás seguro de que no tienes una situación de recursión infinita, es probable que tu algoritmo no sea adecuado para que Ruby lo ejecute de manera recreativa. Convertir un algorythm de una recursión a otro tipo de pila es bastante fácil y te sugiero que lo intentes. Aquí sabrás como podrás hacerlo.
def recursive(params)
if some_conditions(params)
recursive(update_params(params))
end
end
recursive(starting_params)
se transformará 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
Solo tuve el mismo problema y es muy fácil solucionarlo en Linux o en Mac. Como se dijo en las otras respuestas, Ruby usa la configuración de la pila del sistema. Puede cambiar esto fácilmente en Mac y Linux estableciendo el tamaño de la pila. Ejemplo de Fox:
ulimit -s 20000
Yukihiro Matsumoto escribe here
Ruby usa C stack, por lo que necesita usar ulimit para especificar un límite en la profundidad de la pila.