★複数の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の資源を活用して分散処理を行ってみました。
コメントをお書きください