23. June, 2007
11. September, 2007
in by Michael Neumann

Okay, I did a simple performance benchmark on my laptop.

My 31 lines Erlang webserver shown below using the undocumented option {packet, http} as described here is very fast!

-module(http).
-export([start/0]).

start() ->
  {ok, LSock} =
    gen_tcp:listen(8081, [binary, {packet, http}, {reuseaddr, true}, {active, false}, {backlog, 30}]),
  accept(LSock).

accept(LSock) ->
  {ok, Sock} = gen_tcp:accept(LSock),
  Pid = spawn(?MODULE, request(Sock)),
  accept(LSock).

request(Sock) ->
  {ok, {http_request, Method, Path, Version}} = gen_tcp:recv(Sock, 0),
  headers(Sock).

headers(Sock) ->
  case gen_tcp:recv(Sock, 0) of
      {ok, {http_header, _, _, _, _}} -> headers(Sock);
      {error, {http_error, _}} -> headers(Sock);
      {ok, http_eoh} -> body(Sock)
  end.

body(Sock) ->
  gen_tcp:send(Sock, [<<"HTTP/1.1 200 OK\r\n">>,
     ["Connection", ": ", "Keep-Alive", "\r\n",
      "Content-Length: 7\r\n",
      "Content-Type: text/plain\r\n"],
    <<"\r\n">>, <<"1234567">>]),
  request(Sock).

Save this code in a file http.erl. Then start up Erlang "erl" and type "c(http)." followed by "http:start().". After that, you can run apache-bench on it:

    ab -n 50000 -c 100 -k http://127.0.0.1:8081/test

The result on my very old laptop is 3600 requests/sec.

Now the same with Mongrel:

require 'rubygems'
require 'mongrel'

class TestHandler < Mongrel::HttpHandler
  def process(req, resp)
    resp.start(200) do |head, out|
      head["Content-Length"] = "7"
      out.write("1234567")
    end
  end
end

Mongrel::Configurator.new :host => '127.0.0.1' do
  listener :port => 8081 do
    uri "/test", :handler => TestHandler.new
    run
    join
  end
end

The same benchmark with Mongrel gives only around 500 requests/sec. Of course Mongrel does a little more than the simple Erlang web server shown above. And I think Mongrel doesn't employ connection keep alive. But if you see that lighttpd serves only at 1050 requests/sec, then it's impressive what the Erlang web server can handle. Okay, lighttpd has to load the file from disk (7 bytes), that's much less efficient than serving directly from memory.