@m_seki の

I like ruby tooから引っ越し

MoreLindaのrinda_eval

Lindaのeval操作

Lindaでタプルを生成する操作にはoutとevalの二つがあります。out操作はRinda::TupleSpaceのwriteに対応します。
0から9までの二乗根のタプルの生成は次のようにします。

/* C-Linda */
for (i = 0; i < 10; i++)
    out("sqrt", i, sqrt(i));
/* Rinda */
10.times do |n|
  ts.write([:sqrt, n, Math.sqrt(n)])
end

eval操作はout操作にそっくりに見えますが、プロセスが生成される点が違います。なんと新しいプロセス側で引数の評価を行い、その結果からタプルを生成します。次の疑似コードは10個のプロセスを生成して、それぞれが一つのタプルを生成するものです。sqrtは生成されたプロセスで計算されます。

/* C-Linda */
for (i = 0; i < 10; i++)
    eval("sqrt", i, sqrt(i));

ふつうに考えたら、引数の評価をした後にプロセスが生成されそうに見えますが、C-Lindaはライブラリではない(よくしらないけどプリプロセッサか言語の拡張ぽい)のでそんな芸当ができるようです。

Rinda::rinda_eval

eval操作をRubyでポータブルに実装するのはちょっと難しいので、Rinda::rinda_evalはfork()を持つUNIX属だけを対象とすることにしました。このモジュールメソッドは新しいプロセスを生成しブロックを実行します。また、引数のタプルスペースへの参照をブロックへの引数として与えます。ブロックが返したArrayをタプルスペースに追加します。

10.times do |n|
  Rinda::rinda_eval($ts) do |ts|
    [:sqrt, n, Math.sqrt(n)]
  end
end

C-Lindaのevalみたいな字面を提供できなかったのは残念ですが、実用的なAPIになったと思います。この例では計算結果のタプルを生成するものですが、ワーカプロセスの生成に使うことが多いと思います。10個のワーカプロセスを生成する例を示します。これは無限ループですけど適当な条件で終了させてもよいですね。

10.times do |n|
  Rinda::rinda_eval($ts) do |ts|
    while true
      _, arg = ts.take([:request, nil])
      ts.write([:response, do_it(arg)])
    end
    [:not_reached]
  end
end

MoreRindaのリポジトリにはN-Queenをrinda_evalで解く例があります。

rinda_evalはLindaのeval操作と同じ機能を持つものをちょっと違う字面で提供します。evalを使う教科書の例題を簡単に実行できるようになりますから、この本も読み進めやすいと思います。

並列プログラムの作り方

最後にsqrtを生成する例の完全な(実行できる)ソースを載せます。

require 'rinda/tuplespace'
require 'rinda/eval'

place = Rinda::TupleSpace.new
DRb.start_service

10.times do |n|
  Rinda::rinda_eval(place) do |ts|
    [:sqrt, n, Math.sqrt(n)]
  end
end

10.times do |n|
  p ts.read([:sqrt, n, nil])
end

MoreRinda、リンクと募集

MoreRindaはgithub上に置いてあります。lib以下をsite_ruby辺りにコピーするだけで使えます。

install.rbが旧いのがそのままになっていたり、gemがなかったりでみなさんには不便をかけてしまうと思います。つきましてはインストーラやgem化をしてくれる人(というかその結果)を募集します。gemは使ってないのでレビューなしで取り込むことになるけども...。

ちなみにMoreRindaにはTokyoCabinetを使った永続版のTupleSpaceなんかも混じってます。

#書いたのはずいぶん前なので記憶があやふや。

大事なこと

dRubyによる分散・Webプログラミング

買ってね。