Sunday, November 4, 2012

Concurrent Hello World in Go, Erlang and C++

This week I started taking a deeper look at the Go programming language since I am somewhat addicted to scalability, reliability, concurrency and message passing :-). The first thing I always do when playing around with a new software platform is to write a concurrent "Hello World" program. The program works as follows: One active entity (e.g. thread, Erlang process, Goroutine) has to print "Hello " and another one "World!\n" with the two active entities synchronizing with each other so that the output always is "Hello World!\n". Here is the concurrent Hello World program in Go, Erlang and in C++ using the Mindroid framework.

Go
package main

import "fmt"

func main() {
    sayHello := make(chan string)
    sayWorld := make(chan string)
    quitter := make(chan string)

    go func() {
        for i := 0; i < 1000; i++ {
            fmt.Print("Hello ")
            sayWorld <- "Do it"
            <-sayHello
        }
        sayWorld <- "Quit"
    }()

    go func() {
        for {
            var reply string
            reply = <- sayWorld
            if (reply == "Quit") {
                break
            }
            fmt.Print("World!\n")
            sayHello <- "Do it"
        }
        quitter <- "Done"
    }()
 
    <-quitter
}

Erlang
-module(hello_world).

-export([start/0, say_hello/2, say_world/0]).

say_hello(0, WorldPid) ->
    WorldPid ! quit;

say_hello(N, WorldPid) ->
    io:format("Hello ", []),
    WorldPid ! {sayWorld, self()},
    receive
        sayHello ->
            say_hello(N - 1, WorldPid)
    end.

say_world() ->
    receive
        quit ->
            quit;
        {sayWorld, HelloPid} ->
            io:format("World!~n", []),
            HelloPid ! sayHello,
            say_world()
    end.

start() ->
    WorldPid = spawn(hello_world, say_world, []),
    spawn(hello_world, say_hello, [1000, WorldPid]).

C++ (Mindroid)
#include <stdio.h>
#include "mindroid/os/Message.h"
#include "mindroid/os/Handler.h"
#include "mindroid/os/LooperThread.h"

using namespace mindroid;

static const int SAY_HELLO = 0;
static const int SAY_WORLD = 1;
static const int QUIT = 2;

class HelloHandler : public Handler
{
public:
  HelloHandler(const sp<Handler>& worldHandler) : 
    mWorldHandler(worldHandler), mCounter(0) {}

  virtual void handleMessage(const sp<Message>& msg) {
    switch (msg->what) {
      case SAY_HELLO:
        mCounter++;
        if (mCounter <= 1000) {
          printf("Hello ");
          sp<Message> sayWorld =
            mWorldHandler->obtainMessage(SAY_WORLD);
          sayWorld->metaData()->putObject("SayHello",
            obtainMessage(SAY_HELLO));
          sayWorld->sendToTarget();
        } else {
          mWorldHandler->obtainMessage(QUIT)
            ->sendToTarget();
          Looper::myLooper()->quit();
        }
        break;
    }
  }

private:
  sp<Handler> mWorldHandler;
  int mCounter;
};

class WorldHandler : public Handler
{
public:
  virtual void handleMessage(const sp<Message>& msg) {
    switch (msg->what) {
      case SAY_WORLD:
        printf("World!\n");
        msg->metaData()
          ->getObject<Message>("SayHello")
          ->sendToTarget();
        break;
      case QUIT:
        Looper::myLooper()->quit();
        break;
    }
  }
};

int main() {
  sp< LooperThread<WorldHandler> > wlt =
    new LooperThread<WorldHandler>();
  wlt->start();

  Looper::prepare();
  sp<Handler> hh =
      new HelloHandler(wlt->getHandler());
  hh->obtainMessage(SAY_HELLO)->sendToTarget();
  Looper::loop();

  return 0;
}

I think I really like Go (although Erlang is a bit more crafty :-)). Go eases developing scalable and reliable distributed systems while being a good to read language. Since you can also handle much more concurrent activities with one machine using Goroutines (compared to plain operating system threads) it also helps in saving energy.

Links

5 comments:

  1. I did a Common Lisp version of it at http://tapoueh.org/blog/2012/11/04-Concurrent-Hello.html to compare, and the lparallel Common Lisp library author did some work on the topic too at http://lparallel.org/2012/11/05/concurrent-hello-world/

    ReplyDelete
  2. A libccpa example for C++11 would also make a good fit: https://github.com/Neverlord/libcppa

    ReplyDelete
  3. don't understand why you don't use the built in async functionality in C++11?

    ReplyDelete
    Replies
    1. You can use a lot of C++ abstractions to implement a concurrent Hello World. Plain threads and semaphores, Boost fibers, or other async stuff. But I wanted to see the solution using the Mindroid framework since it is much lighter weight (than Boost) and therefore even runs on tiny embedded hardware targets. Furthermore I really like Android's concurrency stuff. But I have to admit that the Mindroid C++ is somewhat bloated here. This is because Mindroid is a concurrency library that focuses on Actors and message passing which is great for real applications that need to scale but not for tiny ones like Hello World.

      Delete
  4. Actors and CSP:
    http://cstheory.stackexchange.com/questions/184/whats-the-difference-between-the-actor-model-of-concurrency-and-communicating-s

    https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/TbVZNLbvZlY

    http://bartoszmilewski.com/2009/07/16/on-actors-and-casting/

    ReplyDelete

Note: Only a member of this blog may post a comment.