Erlang & Ruby — Ring Network

As part of my Erlang self-education, I’m doing a selection of sample problems (from the “Programming Erlang” book) in both Erlang and my language of choice: Ruby. The idea is to explore the differences between these very different languages.

Today’s problem is to write a ring network benchmark. Given a network of N nodes, where each node is pointing to the next in line (or back to the first), each node should forward a received message along, until it has gone around the ring M times (so that a total of N * M messages are sent).

To start with, I came up with an Erlang module to spawn a set of processes, and pass along a message record. It was pretty ugly at first, but with a little refactoring it turned out alright:

-export([start_ring/1, start_message/3]).

    times_sent = 0,
    runtime, wall_clock, owner, note}).

start_ring(N) ->
  spawn(fun() -> 
        FirstPid = start_ring(N1, self()),
        io:format("Starting node ~p (~p) pointing to ~p~n",
          [N, self(), FirstPid]),
        loop(N, FirstPid) end).

start_ring(N, Next) ->
  Pid = start_node(N, Next),
    N > 1 -> start_ring(N1, Pid);
    true  -> Pid

start_node(Id, Next) ->
  spawn(fun() -> 
        io:format("Starting node ~p (~p) pointing to ~p~n",
          [Id, self(), Next]),
        loop(Id, Next)

start_message(Pid, Msg, N) ->
  {Runtime, _} = statistics(runtime),
  {WallClock, _} = statistics(wall_clock),
  Pid ! #message{
    loops_remaining = N,
    runtime         = Runtime,
    wall_clock      = WallClock,
    owner           = Pid,
    note            = Msg}.

loop(Id, Next) ->
    Message ->
      case Message#message.owner == self() of
        false ->
          Next ! Message#message{
            times_sent = Message#message.times_sent+1},
          loop(Id, Next);
        true ->
          case Message#message.loops_remaining == 0 of
            false ->
              Next ! Message#message{
                times_sent = Message#message.times_sent + 1,
                loops_remaining = Message#message.loops_remaining  1},
              loop(Id, Next);
            true -> 
              loop(Id, Next)

print_totals(Message) ->
  {R2, _} = statistics(runtime),
  {W2, _} = statistics(wall_clock),
  U1 = (R2  Message#message.runtime),
  U2 = (W2  Message#message.wall_clock),
  io:format("Sent ‘~p~p times in..~n",
    [Message#message.note, Message#message.times_sent]),
  io:format("  Runtime: ~p milliseconds~n", [U1]),
  io:format("  WallClock: ~p milliseconds~n", [U2]).

Next I applied the same pattern in ruby. I created a Node class, told it where the next node was, and had it forward messages along the proper number of times:

class Node
  attr_accessor :next_node, :number

  def initialize(number, next_node = nil)
    @number = number
    @next_node = next_node

  def self.ring_network(node_count)
    nodes =
    nodes[0] =
    (1..node_count-1).each do |i|
      nodes[i] =, nodes[i-1])
    nodes[0].next_node = nodes[-1]

  def gets_message(msg)
    if msg.owner == self.object_id
      if (msg.passes_remaining-=1) == 0
    msg.owner = self.object_id if msg.owner.nil?

    msg.total_passes += 1

class Message
  attr_accessor :runtime, :wallclock, :note,
    :passes_remaining, :owner, :start_time, :total_passes

  def initialize(note, passes_remaining = 300)
    @total_passes = 0
    @start_time =
    @note = note
    @passes_remaining = passes_remaining

  def print_totals
    puts "Sent #{total_passes} messages in…"
    puts "  WallClock: #{( – @start_time) * 1000} milliseconds"

The first impression is just what you’d expect: Ruby is easier to read, and erlang is *much* faster:

1> Pid = ring_network:start_ring(10).
Starting node 10 (<0.59.0>) pointing to <0.68.0>
Starting node 9 (<0.60.0>) pointing to <0.59.0>
Starting node 8 (<0.61.0>) pointing to <0.60.0>
Starting node 7 (<0.62.0>) pointing to <0.61.0>
Starting node 6 (<0.63.0>) pointing to <0.62.0>
Starting node 5 (<0.64.0>) pointing to <0.63.0>
Starting node 4 (<0.65.0>) pointing to <0.64.0>
Starting node 3 (<0.66.0>) pointing to <0.65.0>
Starting node 2 (<0.67.0>) pointing to <0.66.0>
Starting node 1 (<0.68.0>) pointing to <0.67.0>

2> ring_network:start_message(Pid, hello_world, 300).
Sent ‘hello_world’ 3000 times in..  
  Runtime: 0 milliseconds
  WallClock: 3 milliseconds


irb(main):001:0> node = Node.ring_network(10); msg =!, 300); node.gets_message(msg)

Sent 3000 messages in
  WallClock: 79.336 milliseconds
=> nil

The real difference comes out when you increase the numbers:

1> Pid = ring_network:start_ring(10000).

2> ring_network:start_message(Pid, hello_world, 10000).
Sent ‘hello_world’ 100000000 times in..
  Runtime: 95350 milliseconds
  WallClock: 157455 milliseconds


irb(main):002:0> node = Node.ring_network(100); msg =!, 300); node.gets_message(msg)
SystemStackError: stack level too deep
  from ./ring_network.rb:29:in gets_message
  from ./ring_network.rb:29:in gets_message
  from (irb):2
  from :0

In both cases, we’re using recursion to wait for another message after processing one. In Erlang’s case, though, it compiles tail recursion as a JUMP statement, which doesn’t eat up stack space. Ruby doesn’t do this, which is why I get a “stack too deep” when I up the number of loops. To get around this in ruby, I would have to move towards the Erlang module of forking off separate processes (not just separate instances), each listening for some interprocess communication.

Published by

Patrick Schless

I'm a (mostly) ruby/rails developer, living in Chicago. I work at Braintree, and I have lots of side-projects.

11 thoughts on “Erlang & Ruby — Ring Network”

  1. They’re very natural, so lemons are a great way of bleaching the skin and
    helping to reduce the appearance of your acne scars.

    The good news is that effective natural acne treatments really do exist.

    Lemon juice can brighten the skin, fade freckles, and lighten age spots.

  2. For instance having a nice tan on your face will hide things like wrinkles and blemishes.

    Bronzers are generally added to the spray tanning solution for two purposes
    -. The finest self-tanning product manufacturers have a
    range of tanning creams, lotions, and sprays that you could try to see which suit you best.

  3. You really make it seem so easy with your presentation but I find this
    topic to be really something that I think I would never understand.

    It seems too complicated and very broad for me. I’m looking forward for your next post,
    I’ll try to get the hang of it!

  4. This will help the plumbers provide an accurate assessment of the cost and time required for the work.
    Hence, a reasonable service warranty gets counted as an critical
    trait and top quality of a fantastic plumber.

    Or they may be called in if the sink pipes suddenly decide to break apart.

  5. I’ve been exploring for a little bit for any high
    quality articles or weblog posts on this sort
    of area . Exploring in Yahoo I finally stumbled upon this site.
    Studying this information So i am happy to exhibit that I’ve an incredibly excellent uncanny feeling I discovered just what I
    needed. I so much without a doubt will make certain to do
    not put out of your mind this site and provides it a glance
    on a continuing basis.

  6. o Penile exercises like jelqing or even holding out in erections in a straightened position may also help.
    Many males are not happy with the size of their personal organ. PXXL is one of the
    most effective means for permanent penis enlargement and control premature ejaculation.

  7. This works by having the resin fall through one or several filters
    installed into the bottom of the pollenator.
    com, a leading service provider for fire and water damage cleanups.

    Instead of acting as a physical or mental stress
    reliever it actually increases them.

Leave a Reply

Your email address will not be published. Required fields are marked *