In this third article on performance optimization for Ruby on Rails applications, you'll learn how to run the Action Profiler tool and how to start using caching to improve your application's performance. This article is excerpted from chapter 13 of the book Practical Rails Projects, written by Eldon Alameda (Apress; ISBN: 1590597818).

Running the Action Profiler

The Action Profiler can profile a single Rails action call. It works with any of the three profiler tools for *nix machines: the built-in Ruby profiler, ruby-prof (http://ruby-prof.rubyforge.org/), or ZenProfiler (http://rubyforge.org/projects/zenhacks/). If you want to use ruby-prof or ZenProfiler, you need to install it first.

Here’s an example of running the Action Profiler with the built-in profiler against theshowaction inCatalogController:

$ action_profiler -P Profiler CatalogController#show

-------------------------------------------- Warmup...

Profiling...

%

cumulative

self

 

self

total

 

time

seconds

seconds

calls

ms/call

ms/call

name

26.98

1.02

1.02

204

5.00

6.37

String#scan

12.17

1.48

0.46

408

1.13

1.94

Pathname#initialize

10.32

1.87

0.39

204

1.91

11.62

Pathname#cleanpath_aggressive

5.82

2.09

0.22

424

0.52

2.52

Class#new

5.56

2.30

0.21

2587

0.08

0.08

String#==

4.50

2.47

0.17

306

0.56

10.52

String#gsub

3.70

2.61

0.14

614

0.23

0.44

Kernel.dup

3.44

2.74

0.13

613

0.21

0.21

String#initialize_copy

 
... a lot of output ...
--------------------------------------------

You can see from the output that about half of the time is spent scanning strings and creating path names. However, since the built-in profiler presents everything in a flat output, it is hard to know in which part of the application code the time is actually spent. If you want output with call graphs to see where different methods are called from, consider installing ruby-prof.


Note There are currently some compatibility issues between the Action Profiler and both ZenProfiler and ruby-prof. We hope they will be resolved by the time this book hits the shelves.


RAILSBENCH

Another tool for measuring Rails application performance is Railsbench by Rails performance expert Stefan
Kaes. You can download if from http://railsbench.rubyforge.org/.

If you’re working on Windows, you might find Railsbench more useful than the Action Profiler. By default, it uses the Windows-only Ruby Performance Validator (www.softwareverify.com/ruby/profiler/
index.html), which is said to be the best Ruby profiler around.



Now that we have measured the performance of our application and tracked slow actions, it’s time to do something about them. One of the most common ways to speed up a website is to use some kind of caching. If an often-viewed page needs a large amount of database queries or otherwise expensive calculations to produce its output, storing the output in a cache can make the site a lot more responsive.

Out of the box, Rails sports three caching levels: page, action, and fragment caching. You can also use thecached_modellibrary for caching ActiveRecord objects, when you need to go beyond the usual caching.

Page Caching

Page caching is the fastest of the caching schemes in Rails. With page caching, the cached page is stored as a static HTML file in the document root of the web server and served directly from there on subsequent requests. This means that Rails can be bypassed altogether, and the web server can serve the page in the same way that it serves other static files. This obviously means a huge impact on the performance. Whereas a single Rails process can serve a few dozen non-cached pages per second, a real web server serving static files can easily reach speeds of up to 1000 requests per second.

Should all our actions then be using page caching? Well, no. Page caching stores the output of an action in a file and serves that same file all the time, to all the people. This has many drawbacks. The page is not really dynamic anymore. You can’t use authentication, since Rails is bypassed. You also can’t have any personalization on the page. Therefore, page caching is not really recommended for anything but the most static pages served by a Rails application.

Page caching is also kind of against the shared nothing architecture, since the cached pages are stored on the file system. You could use a networked file system that all the application servers would use as the document root, but you would still be vulnerable to different application servers trying to write the same file at the same time.

However, we do have one page in the Emporium application that hardly ever changes: the About page. Let’s implement page caching for it as an exercise.

It turns out it is extremely easy to use page caching. The only thing we need to do is add a single row to the controller in question, which in this case isapp/controllers/about_controller.rb:

class AboutController < ApplicationController
 
caches_page :index

  def index
    @page_title = 'About Emporium'
  end
end

The next time you load the About page, it is stored inapp/public/about.htmland served from there ever after.

We can use thebenchcommand (part of the Rails Analyzer tool set) to look at the performance of the page without and with caching (repeat the runs several times until the results stabilize). First try it without caching:

$ bench -u http://localhost/about -r 100 -c 10

-------------------------------------------- 100.........90.........80.........70.........60.........50
.........40.........30.........20.........10.........
Total time: 48.2463064193726
Average time: 0.482463064193726
--------------------------------------------

And then runbenchwith caching (remember to restart the application to make the code changes live on a production server):

$ bench -u http://localhost:3000/about -r 100 -c 10

-------------------------------------------- 100.........90.........80.........70.........60.........50
.........40.........30.........20.........10.........
Total time: 5.81317806243896
Average time: 0.0581317806243896
--------------------------------------------


Note  By default caching is turned on only in the production environment. If you want to test it in the development environment, change theconfig.action_controller.perform_cachingparameter totrueinconfig/environments/development.rb.


The difference is noticeable, although not nearly as big as it is in reality, since we’re running thebenchcommand on the same machine and it is consuming part of the processing power itself.

But what if you want to change the page and expire the cache some time? That’s easy, too. Just callexpire_page :action => "index"in the action where you change the page.

Please check back for the next part of this series.