This post originated from an RSS feed registered with Ruby Buzz
by Jonathan Weiss.
Original Post: Performance implications of block&capture helpers
Feed Title: BlogFish
Feed URL: http://blog.innerewut.de/feed/atom.xml
Feed Description: Weblog by Jonathan Weiss about Unix, BSD, security, Programming in Ruby, Ruby on Rails and Agile Development.
Recently there have been again some articles on block and capture helpers in Rails (e.g. this one).
A block/capture lets you write nicer looking helper functions in Rails, especially if you want to render some HTML before and after a given piece of code.
Typical use cases are styled blocks (HTML) that should surround your items.
Assume we have projects that should be displayed on an overview page. You want to render a nice box with the project title and the project description for each project. Often you end up with partials like this:
When you want to re-use this partial for models other than a project and further, sometimes render different HTML in the box_body you create a helper function. The most common approaches are using a start/end combination of helpers and a block/capture helper.
The idea of the start/end helper combination is that you call one helper to create/print all the HTML before your item rendering, render your item, and then call another helper to close all the tags and finish the box:
# application_heper.rb
def start_box(title)
out = "<div class='box'>"
out += "<span class='box_title'>#{h(title)}</span>"
out += "<div class='box_body'>"
out
end
def end_box
"</div></div>"
end
# view
<%= start_box(project.title) %>
<%=h project.description %>
<--! add custom HTML here -->
<%= end_box %>
This works fine but is a bit un-elegant. Further, you can forget the end_box-function and nothing would complain. Your HTML would just be broken.
A much nicer looking solution is the block/capture helper:
# application_helper.rb
def box(title, &block)
out = "<div class='box'>"
out += "<span class='box_title'>#{h(title)}</span>"
out += "<div class='box_body'>"
out << capture(&block) if block_given?
out += "</div></div>"
block ? concat(out, block.binding) : out
end
# view
<% box(project.title) do %>
<%=h project.description %>
<--! more custom HTML here -->
<% end %>
So we are using a block here to pass our box content. The block is created with the do/end style and then passed as an argument to the helper function. The helper function then creates some output, evaluates the block, and then continues to print some HTML strings . By using the block syntax we can never forget to "close" a box as the Ruby interpreter would complain about the missing end keyword. Further, in the helper method we can choose to not render anything. This technique is especially useful for administrative links.
I'm a big fan of the block/capture helper and favor its syntax any time above the start/end way. I just wanted to post here about a disadvantage of the block/capture way that you should be aware of.
During a recent performance analysis for a client I profiled their root page which is an overview page with many HTML-boxed elements. I noticed that it rendered really slow, espically if you had many items on the overview page. After a bit of digging into the rendering, we found out that their block/capture helpers were eating 70-80% of rendering time. The problem is that creating a block (aka a closure), storing its binding (scope and surrounding variables) and then passing around this closure is expensive compared with the "pure" string output helper.
How much more expensive can be shown by this benchmark. I created a new test Rails project with two actions. Each action displays a project, one uses the start/end helper and the other one the block/capture helper. On the X-axis you see the number of helper calls inside the view and on the Y-axis you see the number of requests per second (a reported by ruby script/performance/request -n 1000 -b).
So the block/capture helper style is a lot slower than the simple start/end helper. But it only matters if you use it a lot on a page. With 250 calls on a page, the block/capture style helper has only 20% of the requests per second that the start/end style helper can deliver. 250 calls may seem like a lot but in my case the page displayed 50 boxed items and the page had several other boxed content (e.g. login, stats, ads). If you then add a lot of variables in the closures scope (that need to be included in the binding), the page rendering can get really slow.
I'm not arguing in general against the block/capture, I really like the resulting syntax and flexibility. But as often with syntactic sugar and nicer looking code, you trade it for performance. Most of the time this should not matter but when it does, it shows!.
DISCLAIMER: Those number are not statistically valid and you should not try to get too much out of them. Use them as a hint were to look for slow rendering.