Erlang Notes

dev environment
Login

dev environment

erlperf example

Consider:

-module(shuffle).
-export([do/1, do2/1, do3/1, do4/1]).

do(List) ->
    List2 = cut(List),
    AccInit = {[], [], [], [], []},
    {L1, L2, L3, L4, L5} = lists:foldl(
        fun(E, Acc) ->
            P = rand:uniform(5),
            L = element(P, Acc),
            setelement(P, Acc, [E | L])
        end,
        AccInit,
        List2
    ),
    lists:flatten([L1, L2, L3, L4, L5]).
cut(List) ->
    Rand = rand:uniform(length(List)),
    {A, B} = lists:split(Rand, List),
    B ++ A.

do2(List) ->
    [X || {_, X} <- lists:sort([{rand:uniform(), N} || N <- List])].

do3(L) ->
    do3(list_to_tuple(L), length(L)).
do3(T, 0) ->
    tuple_to_list(T);
do3(T, Len) ->
    Rand = random:uniform(Len),
    A = element(Len, T),
    B = element(Rand, T),
    T1 = setelement(Len, T, B),
    T2 = setelement(Rand, T1, A),
    do3(T2, Len - 1).

do4([])     -> [];
do4([Elem]) -> [Elem];
do4(List)   -> do4(List, length(List), []).

do4([], 0, Result) ->
    Result;
do4(List, Len, Result) ->
    {Elem, Rest} = nth_rest(random:uniform(Len), List),
    do4(Rest, Len - 1, [Elem|Result]).

nth_rest(N, List) -> nth_rest(N, List, []).

nth_rest(1, [E|List], Prefix) -> {E, Prefix ++ List};
nth_rest(N, [E|List], Prefix) -> nth_rest(N - 1, List, [E|Prefix]).

These alternate shuffles can be benchmarked like so:

$ erlc shuffle.erl
$ erlperf 'shuffle:do(lists:seq(1,1000)).' 'shuffle:do2(lists:seq(1,1000)).' 'shuffle:do3(lists:seq(1,1000)).' 'shuffle:do4(lists:seq(1,1000)).' 
Code                                    ||        QPS       Time   Rel
shuffle:do(lists:seq(1,1000)).           1      11510   86909 ns  100%
shuffle:do2(lists:seq(1,1000)).          1       4026     248 us   35%
shuffle:do3(lists:seq(1,1000)).          1       1284     779 us   11%
shuffle:do4(lists:seq(1,1000)).          1        443    2256 us    4%

erlperf doesn't track memory usage. Here's a minimal companion for that:

#! /usr/bin/env escript
main(Args) ->
    Bins = [compile(A) || A <- Args],
    Words = [run(B) || B <- Bins],
    lists:foreach(
        fun({A, [{_Pid, _Count, W}]}) ->
            io:fwrite("~10B - ~ts~n", [W, A])
        end,
        lists:zip(Args, Words)
    ),
    erlang:halt(0).

compile(Code) ->
    Lines = ["-module(f).", "-export([f/0]).", "f() -> " ++ Code],
    Tokens = [
        begin
            {ok, T, _} = erl_scan:string(Line),
            T
        end
     || Line <- Lines
    ],
    Forms = [
        begin
            {ok, F} = erl_parse:parse_form(T),
            F
        end
     || T <- Tokens
    ],
    case compile:forms(Forms, [no_spawn_compiler_process, binary, return]) of
        {ok, _, Bin} -> Bin;
        {ok, _, Bin, _Warnings} -> Bin
    end.

run(Bin) ->
    {module, f} = code:load_binary(f, f, Bin),
    1 = erlang:trace_pattern({f, f, 0}, true, [call_memory]),
    1 = erlang:trace(self(), true, [call, set_on_first_spawn]),
    Self = self(),
    spawn(fun() ->
        f:f(),
        Self ! done
    end),
    receive
        done -> ok
    end,
    {call_memory, Words} = erlang:trace_info({f, f, 0}, call_memory),
    Words.

As used:

$ memused 'shuffle:do(lists:seq(1,1000)).' 'shuffle:do2(lists:seq(1,1000)).' 'shuffle:do3(lists:seq(1,1000)).' 'shuffle:do4(lists:seq(1,1000)).'
     25415 - shuffle:do(lists:seq(1,1000)).
     36744 - shuffle:do2(lists:seq(1,1000)).
   2020372 - shuffle:do3(lists:seq(1,1000)).
    983250 - shuffle:do4(lists:seq(1,1000)).