2010. 12. 9. 16:56

1번은 동시에(simultaneous)를 어떻게 처리해야 할지 모르것다.

해외 답을 봐도 '동시'에 실행하는 답은 없는 것 같은데 먼가 내가 핀트를 잘못잡고 있는듯,

머 어짜피 atomic이라 해도 erlang 내부에서 말이지, chapter 20.1을 보면
"등록된 프로세스를 만들때 마다 우리는 잠재적인 순차적 병목을 만들고 있다" 라고 적힌것을 보면 erlang 내부에서 적당히 처리해주는 것 같다. IO 쪽에서 이 내용이 나오느데 이걸 하드에도 기록하나;

머 하여간 2번 문제는 적당히 풀었다. 

코드가 병렬화 되어 있지 않습니다. 수정한 버전은 나중에 올리겠습니다.

-module(erl8p2).
-export([main/2]).

makeRing(N) ->
    Pids = for(1,N, fun ringNode/0),
    LastElm = lists:last(Pids),
    [LastElm | Pids].

destroyRing(RingElmPids) ->
    [_|T] = RingElmPids,
    lists:foreach(fun(Pid) -> Pid ! die end, T).

ringNode() ->
    spawn(fun loop/0).

loop() ->
    receive
        {ring, [], _Request, Dest, _Result} ->
            % io:format("Last Node:~p ~p~n",[ring, Msg, pid_to_list(self())]);
            Dest ! {ring,_Result},
            loop();
        {ring, [H|T], Request, _Dest, _Result} ->
            % io:format("Received:~p to ~p~n",[ring, Msg, pid_to_list(H)]),
            H ! {ring, T, Request, _Dest, _Result},
            loop();
        die -> void
    end.          

for(N,N,Fun) -> [Fun()];
for(I,N,Fun) -> [Fun()|for(I+1,N,Fun)].
   
sendRingMsg(RingElmPids, Msg) ->
    [H|T] = RingElmPids,
    H ! {ring, T, Msg, self(), []},
    receive
        {ring, Results} -> Results
    end.                          

stopWatch(Fun) ->
    statistics(wall_clock),
    statistics(runtime),
    Fun(),
    {_, Time1} = statistics(wall_clock),
    {_, Time2} = statistics(runtime),
    [Time1, Time2].

main(N,M) ->
    RingElmPids = makeRing(N),
    [Time1, Time2] = stopWatch(fun() -> for(1,M, fun() -> sendRingMsg(RingElmPids, "Hello World") end) end),
    io:format("Ring benchmark tim = ~p (~p) ms~n", [Time1, Time2]),
    destroyRing(RingElmPids).
    
;; 결과는 아래와 같다.                    
21> erl8p2:main(10000,10).
Ring benchmark tim = 7894 (6896) ms

fun이 local function 보다는 느리고 apply보다는 빠른 연산이라고 하는데,
"2.1  Myth: Funs are slow"
http://www.erlang.org/doc/efficiency_guide/myths.html
나중에 fun이나 바꿔야할 것 같다.

...언제나 느끼는 것이지만 짧은 영어 실력에서 나오는 변수명은 참 눈물난다.
그 때문에 짧은 설명은 붙이자면,
makeRing, destoryRing은 ringNode라 불리는 (뒷부분에선 ringElm로 개명한다..?!)
각 ring을 구성하는 element들의 집합으로 구성되는데,
(나중에 고쳐야될 것 같지만) 1->2->3->1 로 끝부분이 이어지도록 구성된다.

끝부분이 이어진다고 먼가 좋은 것이 있는 것은 아니고 그 덕분에 함수 성질에따라
H를 날리거나 그대로 이용한다.
destoryRing은 같은 element를 2번 지우면 안되니까 Head를 뺀 2, 3, 4, 1을 삭제하고,
sendRingMsg은 Head 로 메시지를 보내 다시 Head로 돌아오도록 만들때 이용한다.

loop는 각 element가 실제로 일을 처리하는 넘인데,
Request에 따라 일을 처리하고 Response에 결과를 추가하고 다음 elements(Pids의 Head)로 전송하는 역활을 한다.
여기서는 하는 일이 없으니 걍 Msg 받고 다음 elements로 메시지를 보내는 역활만 한다.
 {ring, Pids, _Request, Dest, _Result}  는 대충 {ID, next Element Pids, Requtest, 마지막 결과 받을넘, 각노드의 결과}를 나타낸 Msg이다.

그니까
        {ring, [], _Request, Dest, _Result} ->
            Dest ! {ring,_Result},

next elemets Pids가 바닥난 한바퀴 돈 상황에서
Dest로 Results를 발신하고

그제서야 전체 Ring에 Request 를 보내 한바퀴 일 시키신
sendRingMsg께서는 결과를 받아보시고 종료한다.

------------------------------------------------------------------------------------------
수정본: spawn 한다음에 작업 마칠때 까지 기다려주려나? 메시지 통신으로 직접 알아야 하나?

-module(erl8p2).
-export([main/2]).

constructRing(N) ->
    Pids = for(1,N, fun ringNode/0),
    LastElm = lists:last(Pids),
    [LastElm | Pids].

destructRing(RingElmPids) ->
    [_|T] = RingElmPids,
    lists:foreach(fun(Pid) -> Pid ! die end, T).

ringNode() ->
    spawn(fun loop/0).

loop() ->
    receive
        {ring, [], _Request, Dest, _Result} ->
            % io:format("Last Node:~p ~p~n",[ring, Msg, pid_to_list(self())]);
            Dest ! {ring,_Result},
            loop();
        {ring, [H|T], Request, _Dest, _Result} ->
            % io:format("Received:~p to ~p~n",[ring, Msg, pid_to_list(H)]),
            H ! {ring, T, Request, _Dest, _Result},
            loop();
        die -> void
    end.          

for(N,N,Fun) -> [Fun()];
for(I,N,Fun) -> [Fun()|for(I+1,N,Fun)].
   
sendRingMsg(RingElmPids, Msg) ->
    [H|T] = RingElmPids,
    H ! {ring, T, Msg, self(), []},
    receive
        {ring, Results} -> Results
    end.                          

timer(start) ->
    statistics(wall_clock),
    statistics(runtime);

timer(stop) ->
    {_, Time1} = statistics(wall_clock),
    {_, Time2} = statistics(runtime),
    [Time1, Time2].

messageRing(RingElmPids) ->
    spawn(fun() -> sendRingMsg(RingElmPids, "Hello World") end).
 
main(N,M) ->
    RingElmPids = constructRing(N),
    timer(start),
    for(1, M, fun() -> messageRing(RingElmPids) end),
    [Time1, Time2] = timer(stop),
    io:format("Ring benchmark tim = ~p (~p) ms~n", [Time1, Time2]),
    destructRing(RingElmPids).

49> erl8p2:main(10000,1000).
Ring benchmark tim = 281 (297) ms

sendRingMsg(RingElmPids, Msg) ->
    [H|T] = RingElmPids,
    H ! {ring, T, Msg, self(), []},
    receive
        {ring, Results} -> Results
    end.    

Posted by newpolaris