Улучшение производительности рендеринга с помощью Jbuilder и Rails 3
Приложение, над которым я работаю, отвечает на большинство запросов объектами JSON или их коллекциями. Мы используем Jbuilder для создания этих ответов. Объем предоставленных данных довольно велик (несколько тысяч объектов в разных вложенных структурах - как только они отформатированы, так и полностью расширены, для типичного ответа имеется до 10 000 строк JSON). По данным NewRelic, этот рендеринг занимает значительное количество времени - около 1/3 от общего времени запроса.
Я ищу какой-то справочник, набор советов или другой ресурс, который поможет мне убедиться, что я получаю наилучшую производительность JBuilder. Мне также интересно, есть ли сравнения производительности для Jbuilder по сравнению с RABL или другими подобными инструментами.
Изменить: я нашел проблему GitHub, которая жалуется на производительность Jbuilder, но единственное фактическое предложение, которое было сделано, - "не использовать Jbuilder". Ну, на самом деле, они использовали немного более сильный язык, но до сих пор нет слов о том, почему Jbuilder так медленно, что-то, что можно сделать, чтобы обойти его или как другие инструменты для одной и той же задачи сравниваются.
Ответы
Ответ 1
jbuilder создает большой хэш, содержащий ваши данные, а затем использует ActiveSupport::JSON
, чтобы превратить его в json. Есть более быстрые json-излучатели, как показано в следующем микро-контролере (убедитесь, что у вас установлены множитель и яджл-рубины)
require 'benchmark'
require 'active_support'
require 'multi_json'
sample = {menu: {
header: "SVG Viewer",
items: [
{id: "Open"},
{id: "OpenNew", label: "Open New"},
nil,
{id: "ZoomIn", label: "Zoom In"},
{id: "ZoomOut", label: "Zoom Out"},
{id: "OriginalView", label: "Original View"},
nil,
{id: "Quality"},
{id: "Pause"},
{id: "Mute"},
nil,
{id: "Find", label: "Find..."},
{id: "FindAgain", label: "Find Again"},
{id: "Copy"},
{id: "CopyAgain", label: "Copy Again"},
{id: "CopySVG", label: "Copy SVG"},
{id: "ViewSVG", label: "View SVG"},
{id: "ViewSource", label: "View Source"},
{id: "SaveAs", label: "Save As"},
nil,
{id: "Help"},
{id: "About", label: "About Adobe CVG Viewer..."}
]
}}
MultiJson.engine = :yajl
Benchmark.bmbm(5) do |x|
x.report 'activesupport' do
1000.times {ActiveSupport::JSON.encode(sample)}
end
x.report 'yajl' do
1000.times {MultiJson.encode(sample)}
end
end
На моей машине это производит
user system total real
activesupport 1.050000 0.010000 1.060000 ( 1.068426)
yajl 0.020000 0.000000 0.020000 ( 0.021169)
т.е. для кодирования объекта образца 1000 раз активная поддержка брала волосы в течение 1 секунды, MultiJson (с использованием двигателя yajl) занимал 21 мс.
JBuilder жестко закодирован для использования ActiveSupport:: JSON, но MultiJSON (жемчужина, которая позволяет вам переключаться между json-библиотеками) представляет собой тривиальное падение и уже является зависимостью ActiveSupport - см. мой вилка jbuilder. Я открыл запрос на перенос, но до тех пор вы можете попробовать использовать эту вилку (или создать свой собственный - это однострочное изменение)
Ответ 2
Рассмотрим переход к Rabl и добавление некоторого caching. Учитывая, что у вас есть тысячи объектов во вложенных структурах, некоторые узлы вашего результирующего JSON могут отображаться как частичные и кэшированные - прирост производительности может быть огромным.
Кроме того, производительность Rabl немного лучше, чем производительность JBuilder, но я считаю, что синтаксис Rabl иногда путается, и я переключусь на JBuilder после реализации кэширования фрагментов.
Ответ 3
Как указано выше JBuilder создает хэш, затем сериализует этот хэш JSON.
То же самое с кэшированием, есть основной хеш, и кеш-хэш объединяется в основной хеш, который все еще нужно преобразовать в JSON.
Мое решение было TurboStreamer. TurboStreamer выводит непосредственно на IO/Stream/String, поэтому пропускает шаг сериализации, который JBuilder (и на первый взгляд это все еще относится к Rabl, и to_json в зависимости от использования).
Для нас это значительно сократило время рендеринга и время GC (из-за создания хэша в jbuilder) и позволяет нам запускать JSON для клиента, пока мы получаем наши результаты. Недостатком является то, что TurboStreamer является немного более подробным и явным.
Тест производительности A (без кэширования):
Тест производительности B (в основном все кэширование):