11. March, 2008
11. March, 2008
in by Michael Neumann

Damien Katz, the creator of CouchDB, writes about what sucks about Erlang.

21. February, 2008
21. February, 2008
in
»
by Michael Neumann

The last day I hacked on another piece of Erlang code. I gave it the name http_hub. It’s not that easy to explain what it actually does. Clients can connect to it, as it behaves for them like a HTTP server. The client’s request is forwarded to a backend server. But no answer is sent back to the client. Instead http_hub waits on another port (e.g. 14000) for another HTTP request from the backend server (usually a single worker node in a cluster will answer), which includes the response that should be sent back to the client. Once this arrives, the client is answered with the included response. If no request on port 14000 arrives within a specified time, an error response is sent back to the client.

The code is here.

1. February, 2008
1. February, 2008
in by Michael Neumann

While hacking Erlang I discovered some pitfalls or non-orthogonalities.

Process ID and registered atom non-orthogonality

In Erlang you can globally register a Process Id (PID) to an atom as shown below. Then you can use the atom to send a message to the process instead of the PID itself. It’s like a global variable for now. But as you can see in the last line, the atom is not accepted when being assigned to a variable. That’s ugly!

Pid = spawn(fun() -> ... end), % spawn a process
Pid ! {msg, "test"},           % send a message
register(test, Pid),           % register process
test ! {msg, "test"},          % send message
P = test,
P ! {msg, "test"}              % doesn't work!

Put function

Because of the non-orthogonality described above, I had to write code like shown below to allow both atoms and PIDs as arguments.

put(Pid, N) when is_atom(Pid) -> put(whereis(Pid), N);
put(Pid, N) -> Pid ! {put, N}, N.

When I compile this file I get the following warning:

./credit.erl:8: Warning: call to put/2 will call erlang:put/2; not put/2 in this module 
  (add an explicit module name to the call to avoid this warning)

This means that I have to explicitly specify the module name in the call to put. But there is a difference (for example in performance) when explicitly specifying the module name. Maybe there is another solution I don’t know about. Probably one should not use put as function name at all.

28. January, 2008
28. January, 2008
in by Michael Neumann

I’m currently reading the great Programming Erlang book. At the end of chapter 8, the author invites the reader to implement a ring benchmark in Erlang. Then implement the same in another language, compare the results and finally blog about it. That’s what I am doing here ;-). I’ve choosen Ruby because it’s the language I am mostly familiar with (and I am too lazy to implement it using C and MPI).

The Ruby version uses Fibers (Co-Routines) and as such needs Ruby 1.9. It has half the number of lines as the Erlang version and is IMHO easier to understand, thanks to Arrays and loops. The Erlang program uses processes and is around 4 times as fast.

The sourcecode for both languages you can find here.

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.