erlangで分散処理プログラミング

★複数のPCに分散して処理する

 

複数台のPCのCPUを使って計算処理を行います。

 

分散処理プログラミングと聞くと難しそうに聞こえますが、

 

erlangでやってみると比較的簡単に書くことができます。

 

複数のコンピュータを用いて計算を行い分散処理プログラミングを

 

体験してみたいと思います。

 

 

 

 

 

★Erlangにおける分散処理

複数のコンピュータでErlangのプログラムを動作させるためには、

 

それぞれのコンピュータにErlangの処理系がインストールされている必要が

 

あります。今回、2台のコンピュータ上でErlangシェルを起動し、

 

分散処理でフィボナッチ数を計算させてみます。

 

手順を以下にまとめます。

 

 

1.それぞれのシェルでErlangのネットワーキングカーネルを起動します。

 

2.各ノード間の接続を確認します。

 

3.リモートノードにfibモジュールをロードします。 

 

4.ローカルノードで分散処理に対応した関数を呼び出します。

 

 

 

 

★ネットワーキングカーネルの起動

 

今回、ネットワーキングカーネルは2台のPCそれぞれ以下のように設定します。

 

 PC1:[email protected]

 

 PC2:[email protected]

 

 ※「ノード名@ホスト名」を指定する。

 

 

 

 

PC1:

C:\app>erl -name [email protected]

 

Eshell V5.9.2  (abort with ^G)

 

([email protected])1>

 

 

 

PC2:

C:\app>erl -name [email protected]

 

Eshell V5.9.2  (abort with ^G)

 

([email protected])1>

 

 

 

★各ノード間の接続の確認

各ノード同士が接続できるように合言葉を設定します。

 

そして、別のPCに起動されたノードと接続が可能かどうかを

 

pingコマンドで確認します。

 

「pong」と返ってくれば接続はOKです。

 

もし、ネットワークを通して相手が認識できない場合、

 

pingの結果「pang」が返ってきます。

 

 

 

PC1:

([email protected])1> erlang:set_cookie('[email protected]', apple).

 

true

 

([email protected])2> net_adm:ping('[email protected]').

 

 pong

 

 

 

 

PC2:

([email protected])1> erlang:set_cookie('[email protected]', apple).

 

true

 

([email protected])2> net_adm:ping('[email protected]').

 

pong

 

 

 

 

PC1、PC2共に、「pong」と返ってきたのでネットワーク上の接続はOKです。

 

 

 

★リモートノードにモジュールをロード

PC1:

 PC1から操作をし、PC2に対し、モジュールをロードさせて実行環境を整えます。

 

 

 

 

([email protected])3> c(fib).

 

{ok,fib}

 

 

 

([email protected])4> {M,B,FN}=code:get_object_code(fib).

 

{fib,<<70,79,82,49,0,0,4,188,66,69,65,77,65,116,111,109,0,

       0,0,141,0,0,0,16,3,102,105,...>>,

     "c:/app/fib.beam"}

 

 

 

([email protected])6> rpc:call('[email protected]', code, load_binary, [M,FN,B]).

 

{module,fib}

 

 

 

 

 

 

 

★ローカルノードから分散処理に対応したモジュールを呼び出す

-module(fib).

-export([fib/1,fib/2]).

-export([cfib/1]).

-export([dfib/1]).

 

fib(0) -> 1;

fib(1) -> 1;

fib(X) -> fib(X - 1) + fib(X - 2).

 

fib(0, P) -> P ! 1;

fib(1, P) -> P ! 1;

fib(X, P) -> P ! fib(X - 1) + fib(X - 2).

 

cfib(0) -> 1;

cfib(1) -> 1;

cfib(X) ->

  S = self(),

  spawn(fib, fib, [X - 1, S]),

  spawn(fib, fib, [X - 2, S]),

  wait_for_result([], 0).

 

wait_for_result(L, 2) -> lists:sum(L);

wait_for_result(L, N) ->

  receive

    Res -> L2 = lists:append(L,[Res]),

    wait_for_result(L2, N + 1)

  end.

 

dfib(0) -> 1;

dfib(1) -> 1;

dfib(X) ->

  S = self(),

  spawn('[email protected]', fib, fib, [X - 1, S]),

  spawn('[email protected]', fib, fib, [X - 2, S]),

  wait_for_result([], 0).

 

 

 

 

 

PC1:

([email protected])7> fib:dfib(30).

 

1346269

 

 

 

 

 

分散処理を組み込んだ関数dfibが正常に実行できることを確認できました。

 

では、分散処理なしの場合と、分散処理ありの場合の関数を実行し、

 

どれくらい処理時間が改善させるのか確認してみます。

 

分散処理なしの関数はfibになります。

 

引数を少し大きめの数値にして実行してみると、

 

 

 

 

PC1:

([email protected])8> timer:tc(fib, fib, [42]).

 

{22641000,433494437} 

 

 

 

 

1CPU(PC1)で処理すると、22.641秒かかりました。

 

次にネットワーク上の別のPCのCPUも使用して、

 

合わせて2CPU(PC1+PC2)で実行すると、

 

 

 

 

PC1:

([email protected])9> timer:tc(fib, dfib, [42]).

 

{14032000,433494437}

 

 

 

 

なんと、14.032秒になりました。約40%処理時間が短縮されました。

 

今回はネットワーク上の別のPCの資源を活用して分散処理を行ってみました。