Close and Go BackBack to Viget

Helpers vs. Partials - A Performance Question

Ben Scofield
Ben Scofield, Development Director, June 25, 2008 4

Have you ever looked in your development log and seen something like the following?

Processing FoosController#show (for ...) [GET]
  Session ID: BAh7CDoOcmV0dXJuX3R...
  SQL (0.000869) SHOW TABLES
  SQL (0.000931) SHOW TABLES
  Image Load (0.000465) SELECT images.* FROM ...
  [...]
Rendered entries/_detail (0.00221)
Rendered entries/_detail (0.00103)
Rendered entries/_detail (0.00118)
Rendered entries/_detail (0.00092)
Rendered entries/_detail (0.00107)
Rendered entries/_detail (0.00069)
Rendered entries/_detail (0.00215)
Rendered entries/_detail (0.00113)
Rendered entries/_detail (0.00113)
Rendered entries/_detail (0.00098)
Rendered entries/_detail (0.00098)
Rendered entries/_detail (0.00087)
Rendered entries/_detail (0.00079)
Rendered entries/_detail (0.00086)
Rendered entries/_detail (0.00079)
Rendered entries/_detail (0.00087)
Rendered entries/_detail (0.00076)
Rendered entries/_detail (0.00081)
Rendered shared/_nav (0.00207)
Rendered shared/_analytics (0.00011)
Rendered shared/_footer (0.00188)
Completed in 1.63867 (0 reqs/sec) | Rendering: 0.40653 (24%) ...

These sorts of entries in the log have always felt like a code smell to me - even when some sort of markup reuse is obviously necessary (so you can't just do it inline), I can't help but think that there's some significant cost to rendering the same file over and over again. So after a long time of thinking about it, I finally got around to doing some benchmarks comparing generating markup in a helper method, rendering a partial repeatedly for a collection, and rendering a partial with the :collection key. For each method, I use apache benchmark to hit a page 1000 times (with 2 simultaneous requests); each request generated 1000 divs like this:

<div>
  <span>index</span>
  <a href="#">link</a>
</div>

Here's what I found:

Method Time per Request    Requests/Second   
Helper 186.998 ms 10.70
Partial 438.244 ms 4.56
Partial for collection    266.068 ms 7.52

So it looks like there is a cost to rendering a partial repeatedly, but that cost can be reduced by using the :collection key, and reduced even further by generating the markup in a helper method. Of course, the helper method can be a smell of its own, but if performance is an issue it may be worth a look.

Note: This is an unscientific test, so feel free to respond with your own benchmarks.

Bruce Williams said on 07/06 at 03:00 PM

Ben,

Excellent post; performance is an excellent issue to highlight between partials and helpers… there’s just a lot more code at work. 

In general, I recommend that people move as much code as possible into helpers vs partials for other reasons; it’s easier to document, reuse, and extend Ruby code, and in the case of small partials there’s very little pain involved.

Of course this falls flat on its face when you need to work in HTML to support designers on staff that struggle with anything outside a template… which is why I try to work with designers who don’t have that problem ;-)

Ben said on 07/07 at 08:45 AM

Thanks for the comment, Bruce - the designer piece in particular is very important, and it’s something that we’ve been trying to optimize for a while now. I think it all comes down in the end to the people involved.

Travis Winters said on 07/09 at 01:14 AM

Nice work Ben, what about caching your partial? Would this change your performance benchmarks? I guess it also depends on what kind of work the partial and helpers are doing. Of course lighter is better, but sometimes your helpers will have to do some work other than rendering a small chunk of html. Isn’t this performance evaluation a bit misleading?

Ben said on 07/09 at 05:14 AM

Interesting points, Travis - caching would certainly change the results, but as a strategy it’s available to both helpers and partials, so I don’t know that it would ever make partials a *better* option than helpers (from a pure performance standpoint).

As for whether these benchmarks are misleading… I don’t think they are. For the case I set here (a very simple one, to be sure), I think the performance numbers hold. I don’t think at all that this is the end of the discussion, however; clearly, many scenarios remain to be tested (adding caching, doing more complicated things in the reused markup, block-accepting helpers vs. partials, etc.), but this is just a place to start.

Thanks for the comment!

Trackback URL: http://www.viget.com/trackback/1174/

Comments for this entry were closed after 60 days.

Name:

Email:

URL:

Not a robot? Prove it by entering the word below.


Remember my personal information

Notify me of follow-up comments?

A Development Community for Viget Labs and Beyond

Every team member here at Viget Labs strives to be an innovator. We members of the development team are no different - that's why we're constantly engaging in community discussions and exploring the unknown that is the next generation of open-source web applications.

Viget Is Hiring!

Viget has job openings for Ruby Developers, Interns, and Front-End Developers. Learn More »

Recent Comments

I think that polymorphic_url(@commentable, :anchor => “comment_#{@comment.id}") should work. You can also refactor the “comment_#{@comment.id}” to a separated method, like dom_id, which returns the dom identifier of the comment.