Today I complained on twitter that the obvious way to start writing an application with Ruby and Sinatra is too slow by default. Substituting a template calling the
erb :index
takes
a few milliseconds (just substituting "hello" in toy template). Benchmarking it with apache benchmark shows how a similar hello world app written in PHP serves 1500 requests per second, while using erb this trivial substitution can handle 250 requests per second (both benchmarks ran on my MBA 11").
Probably there is some lame reason why this happens, like opening the template file again and again or alike (excluding the template the method dispatch of Sinatra + Mongrel is able to serve 600 requests per second, so it is the template substitution).
My point is, it is not ok that by default it is so damn slow. It's not Ruby that is slow, this is not computationally intensive code. Is is simply that nobody cares to provide the basic solution as a fast one apparently. I'm sure that with a few tricks I can handle that, and be happy using Ruby, that is a language that I love semantically, instead of PHP that is a language that I don't like at all.
Many twitter replies were in the tone like "but speed is not equal to scalability".
I disagree about that. If there is something cool about web programming is that often the web side is
trivial to scale conceptually. Just add more web servers, there is no shared data.
Your only bottleneck in a web app should be: the databases, the workers. There are no excuses for the page generation to be slow. In the web
speed is scalability because every web server is conceptually a parallel computation unit. So if the web page generation takes 10 ms instead of 100 ms I can server everything with just 10% of the hardware.
I love Ruby but this is just another instance of "slow by default" that I don't understand very well, and it is not a matter of optimizing for programmer's performance, you can substitute a simple template 1500 times per second in a MBA, how the lame PHP is teaching us.
Edit: the code I used as requested. The main point of this two code fragments is, it should be similar to what most people would write as a first example to accomplish that specific work. The code tries to also be conceptually equivalent, loading the template at every request, and performing a substitution involving parsing the template and evaluating the code.
PHP code:
<?
$user = $_GET['username'];
include("template.php");
?>
The above runs at 1500 requests per second.
tempalte.php is:
<html>
<body>
<? echo("Hello ".$user); ?>
</body>
</html>
Ruby code:
require 'rubygems'
require 'sinatra'
if 1
require 'erubis'
Tilt.register :erb, Tilt[:erubis]
end
before do
# Bla
end
get '/slow/:username' do
@var = "Hello #{params[:username]}"
erb :index
end
get '/fast/:username' do
return "Hello #{params[:username]}"
end
get '/subst/:username' do
f = File.open("template.tpl")
t = f.read
res = t.sub("%content%","Hello #{params[:username]}");
f.close
return res
end
The template is:
Username = <%= @var %>
Yes, just one line with one var.
- /fast is basically as fast as Sinatra/Mongrel dispatch, so reaches 550 requests/second.
- /slow does what the PHP code actually does, and runs at 300 requests/second. This is the sad part. should be more or less like 'fast'.
- /subst is (pretty incredible) much faster than /slow. 450 requests/second, even if the same file is opened again and again in ruby land.
Another data point is this very blog you are reading. It's the lamest PHP (written by me in a day just to have a blog engine with a few special features I liked), using many many MySQL queries per page. It serves 250 pages per second in a blog post with 15 comments, including parsing the post that is done with my own function that processes a markdown-alike stuff to convert it into HTML. The same 250 requests second we have with the default Hello World using the erb template.