Archive for September 10th, 2006

Erlang processes vs. Java threads

Earlier today I ran a simple test of Erlang’s process creation and teardown code, resulting in a rough figure of 350,000 process creations and teardowns per second. Attempting a similar workload in Java gives a figure of around 11,000 thread creations and teardowns per second - to my mind, a clear demonstration of one of the main advantages of Erlang’s extremely lightweight processes.

Here’s the Java code I used - see the earlier post for the Erlang code, to compare:

    // Java 5 - uses a BlockingQueue.
    import java.util.concurrent.*;

    public class SpawnTest extends Thread {
        public static void main(String[] args) {
            int M = Integer.parseInt(args.length > 0 ? args[0] : "1");
            int N = Integer.parseInt(args.length > 1 ? args[1] : "1000000");
            int NpM = N / M;
            BlockingQueue queue = new LinkedBlockingQueue();
            long startTime = System.currentTimeMillis();
            for (int i = 0; i < M; i++) { new Body(queue, NpM).start(); }
            for (int i = 0; i < M; i++) { try { queue.take(); } catch (InterruptedException ie) {} }
            long stopTime = System.currentTimeMillis();
            System.out.println((NpM * M) / ((stopTime - startTime) / 1000.0));
        }

        public static class Body extends Thread {
            BlockingQueue queue;
            int count;
            public Body(BlockingQueue queue, int count) {
                this.queue = queue;
                this.count = count;
            }
            public void run() {
                if (count == 0) {
                    try { queue.put(this); } catch (InterruptedException ie) {}
                } else {
                    new Body(queue, count - 1).start();
                }
            }
        }
    }

21 comments September 10th, 2006 tonyg

How fast can Erlang send messages?

My previous post examined Erlang’s speed of process setup and teardown. Here I’m looking at how quickly messages can be sent and received within a single Erlang node. Roughly speaking, I’m seeing 3.4 million deliveries per second one-way, and 1.4 million roundtrips per second (2.8 million deliveries per second) in a ping-pong setup in the same environment as previously - a 2.8GHz Pentium 4 with 1MB cache.

Here’s the code I’m using - time_diff and dotimes aren’t shown, because they’re the same as the code in the previous post:

-module(ipctest).
-export([oneway/0, consumer/0, pingpong/0]).

oneway() ->
    N = 10000000,
    Pid = spawn(ipctest, consumer, []),
    Start = erlang:now(),
    dotimes(N - 1, fun () -> Pid ! message end),
    Pid ! {done, self()},
    receive ok -> ok end,
    Stop = erlang:now(),
    N / time_diff(Start, Stop).

pingpong() ->
    N = 10000000,
    Pid = spawn(ipctest, consumer, []),
    Start = erlang:now(),
    Message = {ping, self()},
    dotimes(N, fun () ->
               Pid ! Message,
               receive pong -> ok end
           end),
    Stop = erlang:now(),
    N / time_diff(Start, Stop).

consumer() ->
    receive
    message -> consumer();
    {done, Pid} -> Pid ! ok;
    {ping, Pid} ->
        Pid ! pong,
        consumer()
    end.

%% code omitted - see previous post

2 comments September 10th, 2006 tonyg

How fast can Erlang create processes?

Very fast indeed.

1> spawntest:serial_spawn(1). 
3.58599e+5

That’s telling me that Erlang can create and tear down processes at a rate of roughly 350,000 Hz. The numbers change slightly - things slow down - if I’m running the test in parallel:

2> spawntest:serial_spawn(10).
3.48489e+5
3> spawntest:serial_spawn(10).
3.40288e+5

4> spawntest:serial_spawn(100).
3.35983e+5
5> spawntest:serial_spawn(100).
3.36743e+5

[Update: I forgot to mention earlier that the system seems to spend 50% CPU in user and 50% in system time. Very odd! I wonder what the Erlang runtime is doing to spend so much system time?]

Here’s the code for what I’m doing:

-module(spawntest).
-export([serial_spawn/1]).

serial_spawn(M) ->
    N = 1000000,
    NpM = N div M,
    Start = erlang:now(),
    dotimes(M, fun () -> serial_spawn(self(), NpM) end),
    dotimes(M, fun () -> receive X -> X end end),
    Stop = erlang:now(),
    (NpM * M) / time_diff(Start, Stop).

serial_spawn(Who, 0) -> Who ! done;
serial_spawn(Who, Count) ->
    spawn(fun () ->
          serial_spawn(Who, Count - 1)
      end).

dotimes(0, _) -> done;
dotimes(N, F) ->
    F(),
    dotimes(N - 1, F).

time_diff({A1,A2,A3}, {B1,B2,B3}) ->
    (B1 - A1) * 1000000 + (B2 - A2) + (B3 - A3) / 1000000.0 .

This is all on an Intel Pentium 4 running at 2.8GHz, with 1MB cache, on Debian linux, with erlang_11.b.0-3_all.deb.

7 comments September 10th, 2006 tonyg

Calendar

September 2006
M T W T F S S
« Aug   Oct »
 123
45678910
11121314151617
18192021222324
252627282930  

Posts by Month

Posts by Category