Benchmarking Typo

I finally had a bit of time to do some Typo benchmarking over the weekend and (as usual) found that my instincts were all wrong.

I was specifically interested in the performance difference between the page cache and the action cache–my guess was that the action cache was a 10x performance hit.

So I set up a test environment under Xen, running Typo r683, PostgreSQL, Apache 2, FastCGI, Ruby 1.8.2, and Rails 0.13.1. I didn’t do any Apache or Postgres tuning–I just ran them out of the box.

Then I ran ab against a snapshot of scottstuff.net from a couple weeks ago. I used the index page for my testing, as it’s a fairly large page and I wanted to give Typo a real workout.

Here’s what I found:

Cache TypeRequests per second
Page Cache2357
Action Cache10.6
No cache1.01

That really wasn’t what I’d expected. The action cache underperformed my expectations by a factor of 20.

I then did a bit of experimentation. I created a new uncached action in my test Typo setup that did nothing but render :text => 'foo', :layout => false, just to see if the caching system was slowing things down. Result? 10 requests/second. Then I created a new Rails project from scratch and added a new controller with the same action, and saw the same results. Still 10 requests/second.

However, in Rails’s logs, it said that it handled the request in 2 ms, and I should be seeing 500 hits/sec for the “foo” page. So something is adding an extra 98 ms to each request. I’m still hunting for this–I don’t know if it’s something Xen-related on my system, an artifact of my Apache config, or what.

I’ve tried upgrading to Rails 0.14.0, but that’s a whole other article.

Conclusions:

  1. The page cache is really, really fast.
  2. The action cache is a substantial improvement over the uncached case–about 10x on this system–but can’t touch the performance of the page cache.
  3. Changing the concurrency settings on ab and/or the number of FastCGI backends in use didn’t make a substantial performance difference. Settings 2-15 gave roughly the same results.
  4. My caches_action_with_params is slightly faster then the stock action cache.
  5. Moving the action/fragment cache from the FileStore (Typo default) to MemoryStore gives no real performance boost. Moving to the MemCacheStore is a substantial performance hit (~2 hits/sec vs 10 hits/sec).
  6. Adding a new uncached action in ArticlesController that simply returns a fixed string (render :layout => false, :text => 'foo') is no faster then the action cache.
  7. Moving the new action from the previous step to a Controller of its own doesn’t help.
  8. Removing all of the routes except the default :controller/:action/:id route doesn’t help, either.

Frankly, on this hardware, I don’t seem to be able to get more then 10 requests/sec out of Rails no matter what I do. I’m pretty sure that this is a mistake, so I’ll post a followup when I figure out what’s wrong.

Update 1: Another datapoint. Running an example Ruby FCGI on this box gives me 832 hits per second. So whatever the problem is, it’s not fundamental to Ruby FCGI on this box. So I need to look into Rails and see what’s happening.

Update 2: Making some progress. Apparently I screwed up when I tested a new, standalone Rails FCGI app before (forgot to restart FCGI?). This time, I got 130 req/sec, which is a vast improvement over the 10 req/sec that I was seeing before. Most of that speed hit seems to come from using Postgres for session storage. Unfortunately, even after making that change, my null controller is still only getting 40 req/sec with Typo. Transporting the same controller to a blank Rails project gives me 130 req/sec with the same code. Watching strace, it looks like something is forcing Typo to reload the digest/md5 module for every hit. Unfortunately, I can’t figure out how that’s happening–my environment.rb is identical between the two trees, as is my database.yml. I’ll get back to this later; I have other things that I need to finish today.

Posted by Scott Laird Mon, 17 Oct 2005 16:51:13 GMT


Comments

  1. Roberto about 3 hours later:

    I haven’t tried it myself yet, but I think at least it has less memory leaks than fcgi.

  2. Scott Laird about 4 hours later:

    I considered it, but it’s only supposed to be a 10% boost, and I have other problems to deal with first. Once I get FCGI running quickly, then I’ll see what else I can do to get more speed.

  3. dave about 22 hours later:

    Does this give you any concerns over the actual usability of Rails in a heavy-use application? Page caching is ok for blogs, but what about other applications?

  4. Scott Laird about 23 hours later:

    Like most tools, if you want the best performance, then you need to spend some time tuning things. In my case, I discovered that using Postgres for session management is bad for performance. Once I disabled that, I was seeing 30-50 hits/sec, depending on the content. That’s not too bad.

    The big reason that Typo’s uncached behavior is so bad is that we’re still doing ~90 DB queries to generate the front page. We’re working to lower that; hopefully non-blog applications won’t need that much data on most pages.

  5. Dave 1 day later:

    Valid points indeed.

    90 DB queries is not that high - it SOUNDS high, but when you look at actual applications you may have developed for the web, you quickly find that you’re doing more querying than you thought :) Still, would be good to see it come down.

    I’m definitly stoked about Typo, and am excited to watch it develop.

  6. Andrew Beacock 1 day later:

    Please do keep posting your findings here about these setup and caching issues, it’s very interesting reading as I’ve been thinking about porting my blog to Typo and wondering on the best environment in which to let it live.

  7. Stefan Kaes about 1 month later:

    The action caching slowness is probably due to using hashes as arguments for the cache key. Internally these are resolved into url_for calls, which are dog slow for TYpo because Typo has complicated routes. Try using unique strings instead. You should see a big improvement for action caching.

  8. Stefan Kaes about 1 month later:

    Move the require ‘digest/md5’ in cgi/session.rb to the top of the file. That will help.

  9. Kevin about 1 year later:

    Great tip about editing cgi/session.rb to fix the damn md5 bug - I’ve been trying to fix that since rails 0.13!

    I saw it by doing strace -p process, and seeing it require md5 over and over again during most requests.

    I can’t believe a bug like that exists in the base ruby session.rb… and with all the other infanticide monkey-patching of core files we do in rails, we don’t fix that one…