[erlang-questions] mochiglobal vs ets vs registered process

Garrett Smith g@REDACTED
Tue May 22 20:39:23 CEST 2012


This is a followup on a recent question I had about "meta programming"
in Erlang. The gist of the post was on a best practice for extending
functionality in Erlang without using registered processes.

This is a short hand of the sort of use case:

-module(foo).

do_something(Value) ->
    foo_impl:lookup(do_something)(Value).

It led me to experiment with some options for storing/retrieving the
"pluggable" functionality:

- mochiglobal
- ets
- registered process running under proc_lib

The source code for the tests is pasted at the end of this email.

The test was to simply read a "global" value 1M times, after having
first initialized the facility. Times are of the "run" portion only
and do not include the "init" time.

Summary:

- ets was the fastest, baseline of 1x
- mochiglobal was 1.8x slower than ets
- registered process was 7.8x slower than ets

The registered process call (slowest) was, on average, ~2 microseconds
per call slower than ets (fastest). Note that's *micro* seconds.

My conclusion:

Provided the process doesn't block unnecessarily, using a process to
lookup "pluggable" functionality (e.g. an implementation module) is
perfectly good for almost any application. IMO it doesn't warrant the
use of ets.

This may not apply to systems under heavy load, where message passing
overhead could grow non-linearly. A number of tests could be run in
parallel to get a better feel for this.

mochiglobal's use of dynamically generated Erlang modules to serve
"global" values is novel, but it doesn't appear warranted on
performance grounds alone.

The raw test results:

26> timer:tc(tests, run_ets, []).
{307550,ok}
27> timer:tc(tests, run_ets, []).
{302596,ok}
28> timer:tc(tests, run_ets, []).
{313182,ok}
29> timer:tc(tests, run_ets, []).
{300729,ok}

30> timer:tc(tests, run_mochiglobal, []).
{576356,ok}
31> timer:tc(tests, run_mochiglobal, []).
{567248,ok}
32> timer:tc(tests, run_mochiglobal, []).
{541281,ok}
33> timer:tc(tests, run_mochiglobal, []).
{574178,ok}

34> timer:tc(tests, run_server, []).
{2358793,ok}
35> timer:tc(tests, run_server, []).
{2425085,ok}
36> timer:tc(tests, run_server, []).
{2394177,ok}
37> timer:tc(tests, run_server, []).
{2383787,ok}

The test code:

-module(tests).

-export([init_mochiglobal/0,
         run_mochiglobal/0,
         init_ets/0,
         run_ets/0,
         init_server/0,
         run_server/0]).

-define(KEY, mykey).
-define(VALUE, "My Value").
-define(READS, 1000000).

init_mochiglobal() ->
    mochiglobal:put(?KEY, ?VALUE).

run_mochiglobal() ->
    run_mochiglobal(?READS).

run_mochiglobal(0) -> ok;
run_mochiglobal(N) when N > 0 ->
    ?VALUE = mochiglobal:get(mykey),
    run_mochiglobal(N - 1).

init_ets() ->
    ets:new(tests_ets, [named_table]),
    ets:insert(tests_ets, {?KEY, ?VALUE}).

run_ets() ->
    run_ets(?READS).

run_ets(0) -> ok;
run_ets(N) when N > 0 ->
    [{_, ?VALUE}] = ets:lookup(tests_ets, ?KEY),
    run_ets(N - 1).

init_server() ->
    Server = proc_lib:spawn(fun() -> server_loop(?VALUE) end),
    register(server, Server).

server_loop(Value) ->
    receive
        {get, ?KEY, Src} ->
            Src ! {ok, Value},
            server_loop(Value);
        _ ->
            server_loop(Value)
    end.

server_get(Key) ->
    server ! {get, Key, self()},
    receive
        {ok, Value} -> Value
    after
        3000 -> error(timeout)
    end.

run_server() ->
    run_server(?READS).

run_server(0) -> ok;
run_server(N) when N > 0 ->
    ?VALUE = server_get(?KEY),
    run_server(N - 1).



More information about the erlang-questions mailing list