The app I'm working on responds to most requests with JSON objects or collections thereof. We're using Jbuilder to construct those responses. The amount of data rendered is fairly large (several thousand objects in various nested structures - once formatted and fully expanded, there are as many as 10,000 lines of JSON for a typical response). This rendering is taking a significant amount of time - about 1/3 of the total request time, according to NewRelic.
I'm looking for some kind of guide, set of tips, or other resource that will help me make sure I'm getting the best possible performance out of JBuilder. I'm also curious if there are performance comparisons available for Jbuilder vs. RABL or other similar tools.
Edit: I've found a GitHub Issue that complains about Jbuilder performance, but the only actual suggestion anyone's made is 'don't use Jbuilder'. Well, actually, they used slightly stronger language, but there's still no word on why Jbuilder is so slow, what, if anything, can be done to get around it, or how other tools for the same task compare.
jbuilder builds up a big hash containing your data and then uses ActiveSupport::JSON
to turn it into json. There are faster json emitters as the following micro benchmark shows (make sure you have the multijson and yajl-ruby gems installed)
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
On my machine this produces
user system total real
activesupport 1.050000 0.010000 1.060000 ( 1.068426)
yajl 0.020000 0.000000 0.020000 ( 0.021169)
ie to encode the sample object 1000 times active support took a hair over 1 second, MultiJson (using the yajl engine) took 21ms.
JBuilder is hardcoded to use ActiveSupport::JSON, but MultiJSON (a gem that lets you switch between json libraries) is a trivial drop in and is already a dependency of ActiveSupport - see my fork of jbuilder. I've opened a pull request, but until then you could try using this fork (or create your own - it's a one line change)
Consider switching to Rabl and adding some caching. Given you have thousands of objects in nested structures, some nodes of your resulting JSON can be rendered as partials and cached - the performance gain can be huge.
Apart from this Rabl performance is slightly better than performance of JBuilder, but I find Rabl syntax sometimes confusing and I'd switch to JBuilder once it has fragment caching implemented.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With