2008-10-28 11 views
29

de publicar una pregunta desbordamiento de pila en stackoverflow.com, lo divertido :-)Cómo aumentar el tamaño de pila para una aplicación de ruby. aplicación recursiva conseguir: Pila nivel demasiado profundo (SystemStackError)

estoy ejecutando un código de Ruby recursiva y obtener el: "Stack level too deep (SystemStackError)"

(Estoy 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/el 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 el 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.

Edit: Gracias por los comentarios.
Me doy cuenta de que el uso de una función recursiva quizás no sea la forma 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 rastro de la pila de ruby ​​truncado cuando obtiene el error "Stack level too deep" es un poco engañoso.
Al tener 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, una cosa fuerza que bloquea después de poco más de 190 llamadas, pero en realidad es alrededor de 15.000 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

Respuesta

6

Rubí utiliza la pila C por lo que sus opciones incluyen el uso de ulimit o la compilación de rubí con algún compilador/enlazador bandera tamaño de la pila. 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.

+3

Esta respuesta es correcta para las versiones de Ruby anteriores a la 1.9. Para la versión 1.9 o posterior, vea http://stackoverflow.com/a/27510458/238886 –

8

Yukihiro Matsumoto escribe here

Rubí utiliza C pila, por lo que necesita use ulimit para especificar un límite en la profundidad de la pila .

3

Piensa qué 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 echar un vistazo a SICP here para algunas ideas ...

13

Si está seguro de que usted no tiene una situación de recursión infinita, entonces su algoritmo se pobably no es adecuado para Ruby para ejecutarlo de manera recíproca. 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 
3

Sólo tenían el mismo problema y es muy fácil de solucionar 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. Fox ejemplo:

ulimit -s 20000 
11

Esta pregunta y sus respuestas parecen remontarse a Ruby 1.8.x, que utiliza 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.

+4

¡Útil! Por ejemplo, 'export RUBY_THREAD_VM_STACK_SIZE = 5000000' lo configura en 5 MB. –

1

A partir de Ruby 1.9.2 se puede encender optimización de llamada 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 stack overflow 

que evitará el error SystemStackError si la llamada recursiva se encuentra al final del método y sólo 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.

Cuestiones relacionadas