您的位置:首页 > 移动开发

5 Ways to Speed Up Your Rails App

2009-10-30 16:43 441 查看
Ruby is a fast language, and a great one in so many ways, but
nothing in this world is truly free. It’s very easy to do things that
seem inconsequential but that later can bring your application to a
grinding halt. In this post, I’ll outline five important ways that you
can avoid some of the most common problems Rails apps encounter.

Before continuing, a disclaimer: do not take these tips and refactor your code ad-hoc. Take
everything with a grain of salt and perform your own measurements to
determine which pieces of your app are slow. Before making any
performance optimizations, get set up with a profiling tool, like
RubyProf, New Relic, Scout, etc. You always want to know where the most
significant bottlenecks are for you, and focus your efforts there first.

Eager Load Associations

The most common and significant problem that I’ve seen in Rails apps
has been the lack of eager loaded associations. A simple extra
_:include_

when performing ActiveRecord finds will prevent 1+N queries. So for
example, if you are displaying a list of articles on your blog homepage
and want to display the author’s name as well, load the posts with
Post.all(:include => :author)
.
For those complex pages, eager loading works multiple levels deep.
Newer versions of ActiveRecord handle complex eager loading cases much
more elegantly by splitting up a large join query into multiple smaller
queries that make better sense.

Note: only perform the eager load when you actually plan to use the objects, because there’s fairly significant overhead to creating many ActiveRecord objects.

Do Database Work In the Database

In the same vein as the first tip, try leveraging the database when
it makes sense. Relational databases are designed to query large
amounts of data and return results; Ruby is not.

For example, if you want to check if the user currently logged in
has commented on an article, you don’t need to load all the comments
for that article. Iterate through each one, and check whether at least
one comment was created by the current user. Doing this will
instantiate objects for every single comment and then instantly discard
them after the check is done. A much better way to obtain the same
result is to push the logic to the database by doing a SELECT COUNT
statement. ActiveRecord has an easy way to do this:
Article.comments.count(:conditions => ["user_id = ?", current_user.id]) > 0


Do as Little as Possible During the HTTP Request Cycle

You want to be able to return a response to the end user’s request
as quickly as possible, so only do the bare minimum needed to return
the response and defer everything else. Actually sending out an email
is relatively slow and users don’t generally care if emails are sent
during the request cycle or right after.

Whether this is implemented using a simple Ruby thread or a robust,
distributed queuing system like RabbitMQ doesn’t really matter. Rails 3
will ship with a default queuing system, but until then, I suggest
checking out DelayedJob and BackgroundJob.

Know Your Gems and Plugins

As Rails applications get more complicated, a good thing to do is to
use existing plugins and gems instead of recreating the work in house.
This usually introduces a significant amount of new code to the
application that is relatively unknown.

There are many great Rails plugins out there. But before depending
on a new gem or plugin, I suggest at least skimming the source — check
for any craziness. Also be sure you’re using plugins for their intended
purposes — or things are likely to go awry.

Avoid Creating Unnecessary Objects

Every time Ruby’s garbage collector is triggered, Ruby will stop
running your code and start cleaning up unused objects. This process
can take between 100 and 400ms on MRI (JRuby has a better behaved,
tunable garbage collector through the JVM), which is a noticeable
period of time. Avoid this as much as possible. This means avoid creating unnecessary objects. I have already mentioned a couple of ways to do this in the previous tips.

In general, the best way to avoid the unnecessary creation of
objects is to understand how Ruby and the libraries in use work. For
example, understand the difference between these two snippets:

sentences.map  { |s| s.strip  }

sentences.each { |s| s.strip! }


The first snippet creates a new Array object and a new String object
for each element in the Array. The second snippet just mutates the
String objects in the Array without creating new Ruby objects.

Granted, this tip only makes a significant difference when dealing
with large data structures, but it’s a good idea to keep in the back of
your mind whether or not you actually need to duplicate objects. If you
have arrays containing thousands of ActiveRecord objects and use
reject
vs.
reject!
, you’ve just created a second array which could potentially have thousands of objects.

There are many other aspects of a Ruby on Rails application that can
cause bottlenecks; listing them all is obviously impossible. That said,
the most important thing to learn is how to locate these bottlenecks.
Solving them can be handled on a case by case basis.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: