Cómo aumentar el tamaño de pila para una aplicación de rubí. Obtención de la aplicación recursiva: nivel de pila demasiado profundo (SystemStackError)

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

  •  04-07-2019
  •  | 
  •  

Pregunta

Publicar una pregunta de desbordamiento de pila en stackoverflow.com, qué divertido :-)

Estoy ejecutando un código de Ruby recursivo y obtengo: " Nivel de pila demasiado profundo (SystemStackError) "

(Estoy bastante seguro de que el código funciona, que no estoy en una espiral de muerte recursiva infinita, pero ese no es el punto de todos modos)

¿Hay alguna forma de cambiar la profundidad / tamaño de pila permitidos para mi aplicación Ruby?

No lo entiendo muy bien 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. Bajo 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 forma más robusta de hacerlo. Pero ese tampoco es el punto. Simplemente me pregunto si hay una manera de aumentar el tamaño de la pila ... período. Y como mencioné, intenté ejecutar ulimit -s 16000 antes de ejecutar el script ruby ??... sin ninguna mejora ... ¿Lo estoy usando mal?

Edit2: De hecho, estaba teniendo una recursión infinita en un caso de borde del código.
El seguimiento truncado de la pila de rubíes cuando aparece el error " Nivel de pila demasiado profundo " es un poco engañoso.
Al tener un comportamiento recursivo que involucra varias funciones, tiene la impresión de que el número de recursiones es mucho menor de lo que realmente es. En este ejemplo, se podría pensar que se bloquea después de poco más de 190 llamadas, pero en realidad son 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

¿Fue útil?

Solución

Ruby usa la pila C, por lo que tus opciones incluyen usar ulimit o compilar Ruby con un indicador de tamaño de pila de compilador / enlazador. La recursión de la cola aún no se ha implementado y el apoyo actual de Ruby para la recursión no es tan bueno. Dado que la recursión es genial y elegante, es posible que desee considerar sobrellevar las limitaciones del idioma y escribir su código de una manera diferente.

Otros consejos

Esta pregunta y sus respuestas parecen remontarse a Ruby 1.8.x, que utilizó la pila de C. Ruby 1.9.xy posterior usa una máquina virtual que tiene su propia pila. En Ruby 2.0.0 y versiones posteriores, el tamaño de la pila de VM puede controlarse mediante la variable de entorno RUBY_THREAD_VM_STACK_SIZE .

Si está seguro de que no tiene una situación de recursión infinita, entonces su algoritmo no es adecuado para que Ruby lo ejecute de una manera recirsiva. Convertir un algoritmo de recursión a diferentes tipos de pila es bastante fácil y te sugiero que lo intentes. Aquí es cómo puedes 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

Yukihiro Matsumoto escribe aquí

  

Ruby usa C stack, por lo que necesitas   use ulimit para especificar un límite en   profundidad de la pila.

Piensa en lo que está pasando con el código. Como otros carteles han mencionado, es posible hackear el código C del intérprete. Sin embargo. El resultado será que está utilizando más RAM y no tiene ninguna garantía de que no volverá 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 memoisación puede ayudar y otras veces no está utilizando las cosas que está empujando en la pila, en cuyo caso puede reemplazar las llamadas recursivas con el estado mutable.

Si eres nuevo en este tipo de cosas, echa un vistazo al SICP aquí para obtener algunas ideas. ...

Solo tuve 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 configurando el tamaño de pila. Ejemplo de Fox:

ulimit -s 20000

A partir de Ruby 1.9.2 puede activar la optimización de llamadas de cola 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

Eso evitará el error SystemStackError si la llamada recursiva está al final del método y solo el método . Por supuesto, este ejemplo resultará en un bucle infinito en su lugar. Probablemente sea mejor depurar usando una recursión superficial (y sin optimización) antes de ir después de una recursión profunda.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top