『プログラミングErlang』8章 練習問題

8.11の練習問題。これ本の中には答え載ってないのかな…?

自分なりの回答。時間計測部分のコードは省略。

  • コードの簡潔さを優先してリングの”始点”は固定にした
  • リストを配列代わりに使っているので効率が悪い

-module(ring_server0).
-export([construct/1, start/3, prepare/2]).
 
nth0(I, L) ->
    lists:nth(I+1, L).
 
rpc(Pid, Request) ->
    Pid ! {self(), Request},
    receive
        {Pid, Response} ->
            Response
    end.
 
construct(N) when N > 0 ->
    Pids = lists:map(fun(I) -> spawn(?MODULE, prepare, [self(), I]) end,
                     lists:seq(1, N)),
    lists:foreach(fun(I) ->
                          Pid  = nth0(I, Pids),
                          Prev = nth0((I-1+N) rem N, Pids),
                          Next = nth0((I+1) rem N,   Pids),
                          Pid ! {self(), {Prev, Next}}
                          end,
                 lists:seq(0, N-1)),
    nth0(0, Pids).
 
prepare(Builder, Pos)  ->
    receive
        {Builder, {Prev, Next}} ->
            loop(Pos, Prev, Next)
    end.
 
loop(1, Prev, Next) ->
    receive
        {_, {0, Msg, Client}} ->
            %io:format("finishi rotation of ~p~n", [Msg]),
            Client ! {self(), Msg},
            loop(1, Prev, Next);
 
        {_, {M, Msg, Client}} ->
            %io:format("last ~p rotations of ~p~n", [M, Msg]),
            Next ! {self(), {M-1, Msg, Client}},
            loop(1, Prev, Next)
    end;
 
loop(Pos, Prev, Next) ->
    receive
        {Prev, {_M, Msg, Client}=Req} ->
            %io:format("pass ~p to the next node ~p~n", [Msg, Next]),
            Next ! {self(), Req},
            loop(Pos, Prev, Next)
    end.
 
start(Pid, M, Msg) when M >= 0 ->
    rpc(Pid, {M, Msg, self()}).

実行時間の計測。

N=100, M=100
time=0.01, (0.008) seconds

N=10000, M=10
time=10.55, (10.925) seconds

N=10, M=10000
time=0.06, (0.058) seconds

…遅い!明らかにリングの構築部分がボトルネックになっている。効率が悪いことは分かっていたが、これほどとは。

やっぱり線形アクセスじゃいけねえ、ということでこのあたりを見ながらdictを使って construct/1 を書き直し。

construct(N) when N > 0 ->
    Pids = dict:from_list(
             lists:map(fun(I) -> {I-1, spawn(?MODULE, prepare, [self(), I])} end,
                       lists:seq(1, N))),
    lists:foreach(fun(I) ->
                          Pid  = Pids:fetch(I),
                          Prev = Pids:fetch((I-1+N) rem N),
                          Next = Pids:fetch((I+1) rem N),
                          Pid ! {self(), {Prev, Next}}
                          end,
                 lists:seq(0, N-1)),
    Pids:fetch(0).

劇的に改善された。

N=100, M=100
time=0.0, (0.007) seconds

N=10000, M=10
time=0.2, (0.228) seconds

N=10, M=10000
time=0.06, (0.058) seconds

One Response

  1. [...] ring_constructor_list.sh 一番最初に作ったリストによる実装。クソ遅い。 [...]

Leave a Reply