Come aumentare le dimensioni dello stack per un'app ruby. Ottenere app ricorsive: livello dello stack troppo profondo (SystemStackError)

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

  •  04-07-2019
  •  | 
  •  

Domanda

Pubblicando una domanda di overflow dello stack su stackoverflow.com, è divertente :-)

Sto eseguendo un codice Ruby ricorsivo e ottengo: " Livello dello stack troppo profondo (SystemStackError) "

(Sono abbastanza sicuro che il codice funzioni, che non sono in una spirale di morte ricorsiva infinita, ma non è questo il punto comunque)

Esiste un modo per modificare la profondità / dimensione dello stack consentite per la mia app Ruby?

Non capisco se questa è una restrizione in Ruby, poiché l'errore dice "Livello dello stack", il che mi dà l'impressione che Ruby conti in qualche modo i "livelli" dello stack, o se significhi semplicemente che il lo stack è pieno.

Ho provato a eseguire questo programma con Vista e Ubuntu con lo stesso risultato. Sotto Ubuntu ho provato a cambiare la dimensione dello stack con 'ulimit -s' da 8192 a 16000, ma ciò non ha cambiato nulla.

Modifica: Grazie per il feedback.
Mi rendo conto che l'uso di una funzione ricorsiva non è forse il modo più affidabile di procedere. Ma non è nemmeno questo il punto. Mi chiedo semplicemente se c'è un modo per aumentare le dimensioni dello stack ... periodo. E come ho già detto, ho provato a eseguire ulimit -s 16000 prima di eseguire lo script ruby ??... senza alcun miglioramento .. Lo sto usando male?

Edit2: In effetti stavo avendo una ricorsione infinita in un caso limite del codice.
La traccia troncata dello stack di rubini quando viene visualizzato l'errore " Stack level troppo profondo " è un po 'fuorviante.
Quando si ha un comportamento ricorsivo che coinvolge diverse funzioni, si ha l'impressione che il numero di ricorsioni sia molto più basso di quanto non sia in realtà. In questo esempio si potrebbe pensare che si blocchi dopo poco più di 190 chiamate, ma in realtà sono circa 15000 chiamate

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

È stato utile?

Soluzione

Ruby utilizza lo stack C, quindi le opzioni includono l'uso di ulimit o la compilazione di Ruby con un flag di dimensione dello stack del compilatore / linker. La ricorsione della coda non è ancora stata implementata e l'attuale supporto di Ruby per la ricorsione non è così eccezionale. Per quanto sia interessante ed elegante la ricorsione, potresti prendere in considerazione l'idea di affrontare i limiti della lingua e scrivere il tuo codice in un modo diverso.

Altri suggerimenti

Questa domanda e le sue risposte sembrano risalire a Ruby 1.8.x, che utilizzava lo stack C. Ruby 1.9.xe versioni successive utilizzano una macchina virtuale con il proprio stack. In Ruby 2.0.0 e versioni successive, la dimensione dello stack VM può essere controllata tramite la variabile di ambiente RUBY_THREAD_VM_STACK_SIZE .

Se sei sicuro di non avere una situazione di ricorsione Infinita, il tuo algoritmo non è probabilmente adatto a Ruby per eseguirla in modo ricorsivo. Convertire un algoritmo dalla ricorsione in diversi tipi di stack è piuttosto semplice e ti suggerisco di provarlo. Ecco come puoi farlo.

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

recursive(starting_params)

si trasformerà in

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 scrive qui

  

Ruby usa lo stack C, quindi è necessario   usa ulimit per specificare un limite   profondità dello stack.

Pensa a cosa sta succedendo con il codice. Come altri poster hanno già detto, è possibile hackerare il codice C dell'interprete. Però. il risultato sarà che stai usando più RAM e non hai alcuna garanzia di non far saltare di nuovo lo stack.

La soluzione davvero buona sarebbe quella di elaborare un algoritmo iterativo per quello che stai cercando di fare. A volte la memoisation può aiutare e talvolta ti accorgi che non stai usando le cose che stai spingendo in pila, nel qual caso puoi sostituire le chiamate ricorsive con uno stato mutabile.

Se sei nuovo in questo genere di cose, dai un'occhiata a SICP qui per alcune idee ...

Ho appena avuto lo stesso problema ed è molto facile da risolvere su Linux o su Mac. Come detto nelle altre risposte, Ruby utilizza l'impostazione dello stack di sistema. Puoi facilmente cambiarlo su Mac e Linux impostando le dimensioni dello stack. Esempio Fox:

ulimit -s 20000

A partire da Ruby 1.9.2 puoi attiva l'ottimizzazione delle chiamate in coda con qualcosa del tipo:

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

Ciò eviterà l'errore SystemStackError se la chiamata ricorsiva è alla fine del metodo e solo il metodo . Naturalmente, questo esempio si tradurrà invece in un ciclo infinito. Probabilmente è meglio eseguire il debug usando la ricorsione superficiale (e nessuna ottimizzazione) prima di procedere con la ricorsione profonda.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top