Как увеличить размер стека для приложения ruby. Получение рекурсивного приложения: уровень стека слишком глубокий (SystemStackError) - PullRequest
32 голосов
/ 28 октября 2008

Размещение вопроса о переполнении стека на stackoverflow.com, как забавно: -)

Я запускаю некоторый рекурсивный код Ruby и получаю: "Stack level too deep (SystemStackError)"

(Я уверен, что код работает, что я не нахожусь в бесконечной рекурсивной смертельной спирали, но это не главное)

Есть ли способ изменить допустимую глубину / размер стека для моего приложения на Ruby?

Я не совсем понимаю, если это ограничение в Ruby, так как ошибка говорит «Уровень стека», что создает у меня впечатление, что Ruby каким-то образом считает «уровни» стека или просто означает, что стек полон.

Я пытался запустить эту программу под Vista и Ubuntu с одинаковым результатом. В Ubuntu я пытался изменить размер стека с помощью ulimit -s с 8192 до 16000, но это ничего не изменило.

Edit: Спасибо за отзыв.
Я понимаю, что использование рекурсивной функции, возможно, не самый надежный способ. Но это не главное. Мне просто интересно, есть ли способ увеличить размер стека .. период. И, как я уже говорил, я попытался запустить ulimit -s 16000 перед запуском сценария ruby ​​... без улучшений ... Я неправильно его использую?

Edit2: На самом деле у меня была бесконечная рекурсия в крайнем случае кода.
Усеченная трассировка стека рубинов при появлении ошибки "Stack level too deep" немного вводит в заблуждение.
При рекурсивном поведении, включающем несколько функций, создается впечатление, что число рекурсий намного меньше, чем на самом деле. В этом примере может показаться, что он падает после чуть более 190 вызовов, но на самом деле это около 15000 вызовов

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

Ответы [ 7 ]

16 голосов
/ 16 декабря 2014

Этот вопрос и его ответы относятся к Ruby 1.8.x, в котором используется стек C. Ruby 1.9.x и более поздние версии используют виртуальную машину с собственным стеком. В Ruby 2.0.0 и более поздних версиях размер стека виртуальных машин можно контролировать с помощью переменной среды RUBY_THREAD_VM_STACK_SIZE.

13 голосов
/ 28 октября 2008

Если вы уверены, что у вас нет ситуации бесконечной рекурсии, тогда ваш алгоритм вряд ли подходит для Ruby для его рекурсивного выполнения. Преобразовать алгоритм из рекурсии в другой вид стека довольно легко, и я предлагаю вам попробовать это. Вот как вы можете это сделать.

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

recursive(starting_params)

превратится в

stack = [starting_params]
while !stack.empty?
  current_params = stack.delete_at(0)
  if some_conditions(current_params)
    stack << update_params(current_params)
  end
end
8 голосов
/ 28 октября 2008

Юкихиро Мацумото пишет здесь

Ruby использует стек C, так что вам нужно используйте ulimit, чтобы указать ограничение на глубина стека.

6 голосов
/ 28 октября 2008

Ruby использует стек C, так что вы можете использовать ulimit или компилировать Ruby с некоторым флагом размера стека компилятора / компоновщика. Хвостовая рекурсия еще не реализована, и текущая поддержка Ruby для рекурсии не так уж велика. Какая бы классная и элегантная рекурсия ни была, вы можете подумать о том, чтобы справиться с ограничениями языка и написать свой код по-другому.

3 голосов
/ 14 ноября 2011

Только что возникла та же проблема, и ее очень легко исправить в Linux или на Mac. Как сказано в других ответах, Ruby использует настройку системного стека. Вы можете легко изменить это на Mac и Linux, установив размер стека. Пример лисы:

ulimit -s 20000
3 голосов
/ 28 октября 2008

Подумайте о том, что происходит с кодом. Как упоминали другие авторы, можно взломать C-код интерпретатора. Тем не мение. В результате вы будете использовать больше оперативной памяти и не будете уверены, что больше не будете загружать стек.

Действительно хорошим решением было бы придумать итеративный алгоритм для того, что вы пытаетесь сделать. Иногда запоминание может помочь, а иногда вы обнаружите, что не используете то, что помещаете в стек, и в этом случае вы можете заменить рекурсивные вызовы изменяемым состоянием.

Если вы новичок в такого рода вещах, взгляните на SICP здесь для некоторых идей ...

1 голос
/ 08 мая 2017

Начиная с Ruby 1.9.2 вы можете включить оптимизацию хвостового вызова с помощью чего-то вроде:

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

Это позволит избежать ошибки SystemStackError, если рекурсивный вызов находится в конце метода и только для метода . Конечно, этот пример приведет к бесконечному циклу. Вероятно, лучше всего отлаживать с использованием мелкой рекурсии (и без оптимизации), прежде чем идти после глубокой рекурсии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...