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

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

 

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

 

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

 

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

 

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

 

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

 

 

 

 

 

★Erlangにおける分散処理

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

 

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

 

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

 

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

 

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

 

 

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

 

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

 

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

 

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

 

 

 

 

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

 

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

 

 PC1:node01@192.168.0.16

 

 PC2:node02@192.168.0.17

 

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

 

 

 

 

PC1:

C:\app>erl -name node01@192.168.0.16

 

Eshell V5.9.2  (abort with ^G)

 

(node01@192.168.0.16)1>

 

 

 

PC2:

C:\app>erl -name node02@192.168.0.17

 

Eshell V5.9.2  (abort with ^G)

 

(node02@192.168.0.17)1>

 

 

 

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

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

 

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

 

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

 

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

 

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

 

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

 

 

 

PC1:

(node01@192.168.0.16)1> erlang:set_cookie('node01@192.168.0.16', apple).

 

true

 

(node01@192.168.0.16)2> net_adm:ping('node02@192.168.0.17').

 

 pong

 

 

 

 

PC2:

(node02@192.168.0.17)1> erlang:set_cookie('node02@192.168.0.17', apple).

 

true

 

(node02@192.168.0.17)2> net_adm:ping('node01@192.168.0.16').

 

pong

 

 

 

 

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

 

 

 

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

PC1:

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

 

 

 

 

(node01@192.168.0.16)3> c(fib).

 

{ok,fib}

 

 

 

(node01@192.168.0.16)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"}

 

 

 

(node01@192.168.0.16)6> rpc:call('node02@192.168.0.17', 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('node01@192.168.0.16', fib, fib, [X - 1, S]),

  spawn('node02@192.168.0.17', fib, fib, [X - 2, S]),

  wait_for_result([], 0).

 

 

 

 

 

PC1:

(node01@192.168.0.16)7> fib:dfib(30).

 

1346269

 

 

 

 

 

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

 

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

 

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

 

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

 

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

 

 

 

 

PC1:

(node01@192.168.0.16)8> timer:tc(fib, fib, [42]).

 

{22641000,433494437} 

 

 

 

 

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

 

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

 

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

 

 

 

 

PC1:

(node01@192.168.0.16)9> timer:tc(fib, dfib, [42]).

 

{14032000,433494437}

 

 

 

 

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

 

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