Подтвердить что ты не робот

Как увеличить размер стека для рубинового приложения. Рекурсивное приложение: уровень стека слишком глубокий (SystemStackError)

Проводка вопроса на stackoverflow.com, как забавно: -)

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

(я уверен, что код работает, что я не в бесконечной рекурсивной спирали смерти, но это не так).

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

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

Если вы уверены, что у вас нет ситуации с бесконечной рекурсией, тогда ваш алгоритм явно не подходит для 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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

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

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

Ответ 6

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

ulimit -s 20000

Ответ 7

По 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, если рекурсивный вызов находится в конце метода и только метод. Конечно, этот пример приведет к бесконечному циклу. Вероятно, лучше всего отлаживать, используя мелкую рекурсию (и без оптимизации), прежде чем идти после глубокой рекурсии.