Speeding up your app

More and more rails apps are going wild. I’ve been using things like Backpack and Wesabe for awhile now, and I wonder what they are doing for optimization. Some actions really seem to take forever, but as a bystander it’s impossible to tell how much of that is due to traffic and how much is due to possibly inefficient coding. Regardless, as these types of programs get more use by normal people, they will have to be sped up.
Over the course of the last two months, I have been focusing on improving the speed of our rails applications. We (at LoyInc, not e4 industries) built a massive database management system that’s being used by the National Forestry Service as well as the University of Wisconsin-Madison Biostatistics department. This system is also being used by a network of radio stations (where I also work… it’s complicated :) ). Anyway, the dbms basically acts as a back end for all of the websites for the radio stations. It serves queries as XML feeds to the websites, where the data is parsed & made pretty. At the beginning of this process, these data feeds ran at a ridiculously slow 3-5 requests per second. (Zed, sorry about the seat-of-the-pants statistics, if you ever read this.) After a month of tweaking, the same queries run at 480-500 requests per second.

What’d I do?
1. Only select the fields I needed from each query within my runQueryAsXML action

2. Turning off sessions for that specific action (they’re public queries, no session needed)
3. Providing as many url params as possible — making extra lookup queries unnecessary.

4. Reusing results of internal queries

These all pretty much sound obvious, *but* most people do silly things like repeat queries and grab the same associations over and over within an action. Get the hash for the values you need, then reuse that hash throughout. One of the great things about ActiveRecord is that you can grab associations easily, and one of the worst things about ActiveRecord is that you can grab associations easily. As a part of this, only select the fields you really need — If all you need is a field id, then just grab the field ID. There’s no reason to grab more models than you absolutely have to.

Tail your development.log on your actions, then make a note of any query that’s run more than once. Clean up anything that looks inefficient, and then tail some more.

An easy way to debug & get the time it takes to do something (again, i’m no statistician) is to do something like this:

def someAction
tStart = Time.now
myHash = Somemodel.find(:all, :conditions => [something], :select => [somefields])
tEnd = Time.now
logger.debug("query ran in (#{(tEnd.to_f - tStart.to_f).to_s})")
.. blah blah blah
end

Then look at the log, if something seems to take a ridiculous amount of time, see if there’s a way to fix it. This works really well if you’re using gems that you didn’t create also. If something someone else has done takes a ridiculous amount of time, then you can clean it up or write your own tool for the job.

Leave a Reply