Последствия размера стека Ruby's fiber 4kB
Волокна - это относительно новая концепция для меня. Я знаю, что размер стека каждого волокна ограничен 4 КБ, и я продолжаю читать, что мне следует "остерегаться" этого. Каковы реальные последствия этого ограничения в реальном мире?
Редактировать:
Кажется, что это ограничение в 4 КБ не является таким препятствием, и требуется большое количество локальных переменных (4 045) в самом волокне, чтобы вызвать 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:
Модифицированный код выше. Похоже, что переменные и т. Д. В вызываемых методах становятся частью стека волокна. Если это так, то глубина вызова (даже без рекурсии) может быть более серьезной проблемой, поскольку самим методам, вероятно, потребуется больше места в стеке, чем переменным (которые кажутся прозрачными ссылками на объекты в куче).
Следующий код завершается ошибкой на 4 031-й итерации и указывает, что переменные в вызываемых методах становятся частью стека волокна:
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 пережевала несколько сотен МБ ОЗУ.
2 ответа
Следствием этого является то, что вы должны уделять больше внимания памяти вашего кода Fibre, потому что у вас может быть утечка памяти.
Некоторые рекурсивные функции могут вызвать проблемы
Как упомянул в своем ответе Антон, у вас большой объем памяти в волокне. Примеры вещей, которые могут (потенциально) съесть много памяти:
- Большие строки (т.е. строка, содержащая HTTP-ответ приличного размера)
- Рекурсивные функции (уровень стека слишком глубокий!)
- Потоки или объекты, подобные потокам: будьте ОЧЕНЬ осторожны с потоковыми буферами; если они приблизятся или превысят 4k, вы начнете видеть очень странное поведение