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

Например, наше средненькое рельсовое приложение при загрузке делает require около 2200 раз — это где-то совсем в правой части графика. Совсем никуда не годится. На 1.9.2 приложение стартует за 20 секунд, а на 1.9.3 уже 46. Слишком медленно!
Этому есть несколько причин, но главная — алгоритм require, который выглядит примерно вот так:
Все тормоза идут от цикла, и чем больше файлов мы подключаем, тем сильнее все тормозит. Я написал патч для 1.9.3, который заменяет тормозной цикл на что-то подобное:
И это дает примерно следующий результат:

Намного приятнее!
Это графики синтетического теста по подключению пустых файлов, но патч дает ускорение и в реальных проектах. Мое приложение теперь грузится около 10 секунд. На 1.9.2 оно грузилось 20. Пустое приложение загружается вообще за 1.1 сек, что даже быстрее, чем в 1.8.7

Тут все достаточно просто, это займет не больше 10 минут, если у вас установлен RVM.
Мне нужно привлечь как можно больше внимания к патчу, прежде чем его включат в основной транк. Сильно помогло, если бы вы:
Я полагаю, что до включения этого патча в 1.9.3 еще далеко, предстоит много работы, но все же, это первый из многих шагов, направленных на ускорение запуска приложений на рельсах. Есть еще Bundler и RubyGems, которые тратят очень много времени непонятно на что — хотелось бы изучить их внутренности.
Так же я планирую портировать этот патч в JRuby, поскольку там есть похожие проблемы. В Rubinius, кажется, изначально с этим все в порядке.
Последние релизы MRI Ruby показывают значительное замедление при подключении файлов.

Например, наше средненькое рельсовое приложение при загрузке делает require около 2200 раз — это где-то совсем в правой части графика. Совсем никуда не годится. На 1.9.2 приложение стартует за 20 секунд, а на 1.9.3 уже 46. Слишком медленно!
Этому есть несколько причин, но главная — алгоритм require, который выглядит примерно вот так:
def require(file)
$loaded.each do |x|
return false if x == file
end
load(file)
$loaded << file
end
Все тормоза идут от цикла, и чем больше файлов мы подключаем, тем сильнее все тормозит. Я написал патч для 1.9.3, который заменяет тормозной цикл на что-то подобное:
def require(file)
return false if $loaded[file]
load(file)
$loaded[file] = true
end
И это дает примерно следующий результат:

Намного приятнее!
Это графики синтетического теста по подключению пустых файлов, но патч дает ускорение и в реальных проектах. Мое приложение теперь грузится около 10 секунд. На 1.9.2 оно грузилось 20. Пустое приложение загружается вообще за 1.1 сек, что даже быстрее, чем в 1.8.7

Как пропатчиться
Тут все достаточно просто, это займет не больше 10 минут, если у вас установлен RVM.
# Сначала замеряем на текущей версии
cd /your/rails/app
time script/rails runner "puts 1"
# Ставим патченный ruby
curl https://gist.github.com/raw/996418/e2b346fbadeed458506fc69ca213ad96d1d08c3e/require-performance-fix-r31758.patch > /tmp/require-performance-fix.patch
rvm install ruby-head --patch /tmp/require-performance-fix.patch -n patched
# ... и идем пить чай — эта штука собиралась 8 минут на моем MBP
# Смотрим как улучшилось время
cd /your/rails/app
rvm use ruby-head-patched
gem install bundler --no-rdoc --no-ri
bundle
time script/rails runner "puts 1"
Чем можно помочь
Мне нужно привлечь как можно больше внимания к патчу, прежде чем его включат в основной транк. Сильно помогло, если бы вы:
- Попробовали патч на своих приложениях и отписали время выполнения бенчмарков в комменты
- Посмотрели код в пулл-реквесте на гитхабе (язык С, но я надеюсь что никто не испугается)
- Попробовали на Windows
- Репортили баги, которые найдете
Что дальше
Я полагаю, что до включения этого патча в 1.9.3 еще далеко, предстоит много работы, но все же, это первый из многих шагов, направленных на ускорение запуска приложений на рельсах. Есть еще Bundler и RubyGems, которые тратят очень много времени непонятно на что — хотелось бы изучить их внутренности.
Так же я планирую портировать этот патч в JRuby, поскольку там есть похожие проблемы. В Rubinius, кажется, изначально с этим все в порядке.