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