Other Systems

The remainder of this chapter is a collection of miscellaneous performance tips and solutions to common problems. If you have specific trouble, the Rails wiki (http://wiki.rubyonrails.com/) might help. The wiki is disorganized at times, but it has a large amount of relevant information on many topics if you are willing to search.

A large part of software development consists of selecting the right tools for the job. This encompasses not only languages but libraries, frameworks, source control, databases, servers, and all of the other tools and materials that go into a completed application.

Writing Ruby extensions in C used to be hard. If you wanted to rewrite performance-sensitive functions, there were many things besides the actual code that you had to deal with. Not so anymore.

Ryan Davis has unleashed an incredible tool, RubyInline, [61] for integrating C with Ruby. This tool allows you to embed C/C++ code as strings directly within an application. The strings are then compiled into native code (only to be recompiled when they change) and installed into your classes. The canonical example, the factorial function, shows just how fast and clean this can be:

	require 'rubygems'
	require 'inline' # gem install RubyInline
	require 'benchmark'

	class Test
	  # Standard Ruby factorial function

	  def factorial(n)
	    result = 1 
	    n.downto(2) { |x| result *= x } 
	    result
	  end

	  # Reimplemented in C (compiled on the fly)
	  inline do |builder| 
	    builder.c <<-EOINLINE
	      long factorial_c(int max) {
	        int i = max,
	            result = 1; 
	        while (i >= 2) { result *= i--; }
	        return result;
	      }
	    EOINLINE 
	  end 
	end

We can then set up a benchmark to compare the two implementations:

	t = Test.new

	Benchmark.bmbm do |b|
	  b.report("Ruby factorial") do
	    200_000.times { t.factorial(20) }
	  end

	  b.report("C factorial") do
	    200_000.times { t.factorial_c(20) }
	  end
	end

On my machine, the C implementation is extremely fast—more than 25 times the speed of the standard Ruby implementation!

	                 user     system      total         real
Ruby factorial   2.760000   0.010000   2.770000  (  2.753621)
C factorial      0.110000   0.000000   0.110000  (  0.104440)

The best part of RubyInline is that it keeps your code clean. Ruby and C code addressing the same area can be intermingled, rather than being spread across multiple files. And RubyInline handles the type conversion for you—you can deal with ints, longs, and char*s, and they will automatically be converted to and from Ruby types.

Email delivery can be a difficult and aggravating aspect of a deployed application. The SMTP protocol was not designed to withstand the types of attacks that are being directed at the mail system today, and so delivering mail can be a more complicated process than it may seem.

One common problem is that email delivery via SMTP is quite slow, on the average. In addition, it is an unknown; the time it takes to send one email is highly variable. Even when delivering to an SMTP relay on the local network (which is a good idea for high-volume sites), SMTP delivery is slow.

To counteract this slowness, it is usually desirable to decouple the email sending from the web request/response cycle. It makes sense to allow the user to continue working, even if the server is still trying to send email in the background. One option is to simply fork off a separate OS process, or use a separate interpreter thread (via Thread.new with a block) to send email asynchronously. However, this solution does not scale well, as you must handle any concurrency issues that arise on your own. In addition, you have overhead from starting a new thread or process on each piece of mail. For high-volume mail situations, you want a mailer daemon running a tight loop that can send mail without having to start a worker process.

The scalable option is the Robot Co-op's ar_mailer. [62] This little library uses the data-base as an outgoing mail spool. When mail is to be sent, rather than delivering it externally, Rails just dumps it into the database. The separate ar_sendmail process picks it up and sends it along. This way, the application does not get backed up because of slow SMTP performance. ar_sendmail can be run periodically (from cron) or continuously, as a daemon.