Последствия размера стека Ruby fiber 4kB

Волокна для меня - относительно новое понятие. Я знаю, что размер стека каждого волокна ограничен 4 КБ, и я продолжаю читать, что мне следует «остерегаться» этого. Каковы именно последствия этого ограничения в реальном мире?

Редактировать:

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

count = 0
loop do
  count += 1
  puts count
  varlist = String.new
  count.times do |i|
    varlist += "a#{i} = 1\n"
  end
  s = "fiber = Fiber.new do \n #{varlist} \n end \n fiber.resume"
  eval(s)
end

Не самый элегантный код, но он, похоже, демонстрирует ограничения стека волокна. Кажется, что в стек помещаются только возвращаемые значения, локальные переменные (все из которых содержат ссылку на объект в куче) и вызовы методов. Я не проверял, являются ли локальные переменные и т. Д. В методах, вызываемых из волокна, частью стека волокна.

Изменить 2:

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

Следующий код не работает на 4031-й итерации и указывает, что переменные в вызываемых методах становятся частью стека волокна:

count = 0
loop do
  count += 1
  puts count
  varlist = String.new
  count.times do |i|
    varlist += "a#{i} = 1\n"
  end
  m = "def meth\n #{varlist} \n end"
  eval(m)
  fiber = Fiber.new do
    meth
  end
  fiber.resume
end

Изменить 3:

Просто попробовал запустить исходный пример кода на Rubinius 2.0. Его волокна, похоже, не имеют ограничения стека в 4 КБ, хотя после примерно 3500-й итерации он становится все более и более медленным, а примерно на 5000-й итерации он в среднем составляет около одной итерации в секунду. Я не знаю, есть ли предел для RBX, потому что я прекращаю выполнение чуть более чем через 5100 итераций. RBX также использует в несколько раз больше памяти, чем MRI 1.9.3.

JRuby 1.7 также, похоже, не имеет размера стека 4 КБ для волокон, и если волокна имеют максимальный размер стека, мне это неизвестно. Я без проблем выполнил 5000 итераций первого примера кода, хотя, как и следовало ожидать, JVM потратила несколько сотен МБ ОЗУ.


person Matty    schedule 30.11.2012    source источник
comment
может быть, вам следует избегать глубокого рекурсивного потока?   -  person Ron Klein    schedule 30.11.2012
comment
Не могли бы вы опубликовать ссылки, в которых рекомендуется соблюдать осторожность?   -  person Peter Brown    schedule 30.11.2012
comment
@Beerlington Вот пара, которую я смог найти (и еще одну, о которой я не могу наткнуться): github.com/mperham/rack-fiber_pool/issues/17 и lists.basho.com/pipermail/riak-users_lists.basho.com/2012-March/   -  person Matty    schedule 30.11.2012


Ответы (2)


Следствием этого является то, что вы должны уделять больше внимания памяти вашего оптоволоконного кода, потому что у вас может быть утечка памяти.

Некоторые рекурсивные функции могут вызвать проблемы

person Anton Garcia Dosil    schedule 30.11.2012

Как сказал Антон в своем ответе, вы используете код с интенсивным использованием памяти внутри волокна. Примеры вещей, которые могут (потенциально) съесть много памяти:

  • Большие строки (например, строка, содержащая HTTP-ответ приличного размера)
  • Рекурсивные функции (слишком глубокий уровень стека!)
  • Потоки или потокоподобные объекты: будьте ОЧЕНЬ осторожны с буферами потоков; если они приблизятся или превысят 4k, вы начнете наблюдать очень странное поведение
person Damien Wilson    schedule 30.11.2012
comment
Вы уверены, что строки живут в стеке? Я думал, они живут на куче; только ссылки на них, живущие в стеке. - person Wayne Conrad; 30.11.2012
comment
Уэйн: Возможно, я пришел к неверному выводу, но я видел ошибки на уровне стека при работе с раздутыми волокнами. - person Damien Wilson; 30.11.2012
comment
Насколько мне известно, статические переменные объявлены в стеке, а динамические переменные (например, из возвращаемых значений), методы и атрибуты определены в куче. Итак, если строка определена внутри метода, она находится в стеке, если это, например, атрибут класса, она находится в куче. - person Anton Garcia Dosil; 30.11.2012
comment
Похоже, что все строки и другие объекты размещены в куче. Исключение возникает, когда волокно содержит 4045 переменных, что указывает на то, что волокно имеет некоторые накладные расходы и что сама переменная (а не объект, на который она ссылается) занимает один байт. - person Matty; 30.11.2012
comment
Все это очень интересно. Кто-нибудь знает, где задокументировано это поведение? Я полагаю, мы все имеем в виду МРТ, верно? - person Damien Wilson; 30.11.2012
comment
@DamienWilson Насколько мне известно, это официально не задокументировано. Все, что я узнал до сих пор (см. Правки к вопросу), было результатом написания кода и попыток что-то сломать. Я полагаю, что единственная истинная документация - это исходный код C. У меня МРТ 1.9.3. - person Matty; 30.11.2012
comment
Ruby Under a Microscope документирует, как строки хранятся в MRI 1.9. Когда Ruby сохраняет строковое значение в стеке YARV - или, кстати, на любом объекте - он фактически помещает только ссылку на этот объект в стеке. Фактическая структура строки вместо этого сохраняется в куче. - person Wayne Conrad; 01.12.2012