Ruby vs Elixir -- An Unfair Fight (and Elixir wins)

From the Ruby Weekly mailer, I found this post: Elixir vs Ruby Showdown - Phoenix vs Rails.  As a little background:

- Elixir is a wrapper on Erlang to make Erlang more palatable to human beings.  Raw Erlang should be consumed by no one, neither man nor beast, in the course of an afternoon. Elixir tames the beast by doing what Erlang does best -- writing macros in Erlang to add features to Erlang to make Erlang less Erlang.

- Phoenix is a nice little framework built in Elixir using the Erlang HTTP and Websockets library, Cowboy. Cowboy is ridiculously powerful as simple HTTP servers go.  It's received some serious love lately.  And, of course, Cowboy is built on Erlang/OTP and inherits all the power and flexibility Erlang/OTP provides.  It is, after all, the reason we learn Erlang and then wrap it in Elixir in the first place.

- To the article's credit, the ruby configuration is the most optimal for a current ruby implementation that still runs on MRI ruby.  

Unsurprisingly, the Elixir/Phoenix implementation crushes the Ruby/Rails implementation by an order of 10x and was resource constrained by the amount of pipe available to it for getting new requests.  This was not a fair fight.

- Erlang/OTP can theoretically scale to handle a nigh-infinite number of requests.  Erlang/OTP uses a lightweight concurrent threading model inside of a running VM within the process. Whenever a new request arrives, Erlang/OTP doesn't bother with thread pools or thread management. It simply throws off another new hyper lightweight thread to manage the request, manages the request functionally with no memory sharing between threads, and throws it away at the end of the request.  It will eventually run into hardware limitations - network bandwidth first, memory second, and then processor - but joining a second Erlang/OTP machine to the first to build a cluster scales linearly.  

- The Ruby/Rails process is confined to four worker processes, each which are single threaded and limited by Ruby's global interpreter lock.  Although the memory management and garbage collection algorithms are enormously improved in Ruby 2.1.x, it can still only manage those four requests at a time.  And Rails itself is a horrendous memory hog of a framework carrying with it an entire universe of baggage simply to get it started and listening on a socket. Puma is fast; requests are likely sitting in the connection queue waiting for ruby to finish putzing around and serve them.

This isn't a fair comparison: it is like comparing a Ford Pinto to a Ferrari and then pointing out that the Pinto tried to keep up but its engine lit on fire.  In no world would the limited, memory heavy Ruby worker processes keep up with Erlang/OTP's infinite lightweight threading model.  Erlang/OTP was designed by Ericsson from the ground up to be hyper fast to maintain highly distributed telecommunication systems.  Ruby was designed to be fluid and friendly by compromising speed and Rails was designed to provide a single server LAMP stack using a standard MVC2 architecture.  

I applaud the community's push to move off Ruby/Rails and onto real, robust, functional systems.  Phoenix looks small but promising; in a year it will be large and super promising.  I will likely build something small in it to take it out for a spin.  Erlang is a bear but Elixir makes it less so.  And Erlang is so overwhelmingly powerful that the simple prospect of turning, say, a 20 node web cluster into a 2 node web cluster with double the uptime is worth the look.

(I still think that Clojure/Ring-Compojure/JVM is the actual upgrade path for Ruby/Rails... but you know, me and Erlang...)