Улучшение производительности рендеринга с помощью 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 (в основном все кэширование):