If you are into programming languages, you’ve probably heard people talk about how slow Ruby is, and how you should use Python instead. I’ve always loved Ruby’s beautiful syntax. On the other hand, Python isn’t that ugly, and it’s hard to argue with raw speed.
Ruby 1.9 brought major performance improvements to the table, and recently I got a chance to sit down and do some web service benchmarking. Based on my tests, the tradeoff between beauty and performance isn’t as big as it used to be, but there’s still some work to be done.
Here is what I did.
First, I implemented basic “Hello World” apps using the standard web server interfaces for each language; WSGI for Python and Rack for Ruby.
# Ruby: Simple Rack app
require 'rack'
class HelloWorld
def call(env)
[200, {"Content-Type" => "text/plain"}, ["Hello Rack!"]]
end
end
# Python: Simple WSGI app
def simple_app(environ, start_response):
start_response('200 OK[', [('Content-type','text/plain')])
return ['Hello world!\n']
I also tested a simple Python app using Rawr, a fairly well-tuned WSGI/webob micro framework that I developed for Rackspace. Last–but not least–I also benchmarked a simple “Hello World” Sinatra app so I would have something to compare against Rawr.
# Ruby: Sinatra app showing tested route require 'sinatra' get '/hello' do 'Hello world!' end
# Python: Rawr app showing tested route
from rax.http import rawr
class HealthController(rawr.Controller):
"""Provides web service health info"""
def get(self):
self.response.write("Alive and kicking!\n")
class TestApplication(rawr.Rawr):
"""Test class for encapsulating initialization"""
def __init__(self):
rawr.Rawr.__init__(self)
# Setup routes
self.add_route(r"/health$", HealthController),
app = TestApplication()
Ruby vs. Python: Web Performance
Everything was benchmarked by running multiple iterations of apache-bench from the terminal on my MBP. I used Green Unicorn as my test WSGI server, and Thin for Rack apps.
WSGI (PyPy 1.6.0): ~5300 req/sec
WSGI (Python 2.6.1): ~3200 req/sec
WSGI (Python 2.7.2): ~3000 req/sec
Rack (Ruby 1.9.2 MRI): ~4500 req/sec
Rack (Ruby 1.8.7 MRI): ~4050 req/sec
Rawr/WSGI (PyPy 1.6.0): ~4900 req/sec
Rawr/WSGI (Python 2.6.1): ~2750 req/sec
Rawr/WSGI (Python 2.7.2): ~2700 req/sec
Sinatra/Rack (Ruby 1.9.2 MRI): ~1900 req/sec
Sinatra/Rack (Ruby 1.8.7 MRI): ~1399 req/sec
Ruby vs Python: Conclusions
Rack perf beats WSGI hands-down for standard Python (hooray!), although it is unclear how much of the difference is due to Green Unicorn vs. Thin. PyPy is astoundingly fast, but Ruby 1.9.2 isn’t too far behind.
Also, it was interesting to see what a difference Ruby 1.9 made when there was a lot of code to run through (Sinatra). This difference can also be seen in the Rawr tests with PyPy vs. standard Python.
The bad news is that Sinatra was significantly slower than Rawr (booh!). Given the benchmarks for a straight Rack app, I have to wonder whether a Ruby port of Rawr is in order.
Note: This article outlines a simple test of GETs returning small amounts of text. Before choosing a language for your next project, you will undoubtedly want to do more extensive testing, especially around concurrency, object serialization, request/response body size, and POST/PUT performance. You should also see how things look when you put everything behind a reverse proxy and/or load balancer, such as Nginx.