2011年1月6日木曜日

【翻訳】アクターによる並列性、そしてRubyでGoroutine

Ilya Grigorikさんの "Concurrency with Actors, Goroutines & Ruby" を翻訳しました。
元記事はこちら: http://www.igvita.com/2010/12/02/concurrency-with-actors-goroutines-ruby/
(翻訳の公開と画像の利用は本人より許諾済みです)

翻訳の間違い等があればブログコメントやTwitter(@oshow)などで遠慮無くご指摘ください。

アクターによる並列性、
そしてRubyでGoroutine


ruby-go 並列コンピューティングの世界は複雑なものだ。私たちはハードウェアランタイム、そして半ダースもの異なるモデルとプリミティブから選択することを考えなければならない: fork / wait、スレッド、シェアードメモリ、メッセージパッシング、セマフォ、そしてトランザクションなど。ゆえに、Bruce Tate が彼の最近の本(Seven Languages in Seven Weeks)でのインタビューで Matz に、もし過去に戻れるならば Ruby にどんな機能変更を行いたいかと聞いた時、その答えに説得力があったのは驚くべきことではない: 「私はスレッドを取り除いて、アクターなどのより先進的な並列機構を追加するだろう。」

プロセス計算と先進的並列性


 Matz の言明から多くを読み取るのは簡単だが、追求すべき疑問はこうだ:より先進的な並列機構とは? 毎年何千人ものプログラマがスレッド、ミューテックス、セマフォについて教えられてから学校を卒業していく――それらは私たちがどう並列を行うかというものだ! 「先進的な並列機構」というトピックに関して私たち皆が逃している重要な講義があるのか?

答えはたぶん違って、シェアードメモリモデルの「成功」によるところが大きい。プロセス計算(Process calculi)は並列システムの振る舞いをモデリングするアプローチに関係する多くの研究のための正式名称で、多くの代替物を提供している: わずかな例をあげると、CCSCSPACP、そしてアクターモデルだ。しかしながら、そんなわずかな頭文字言葉たちしか私たちの辞書に届いていなかった。それらのほとんどのルーツは1970年代前半に遡るのだから驚きだ――まったく新しいものではないのだが、最近まで滅多に言及されなかった。

アクター、CSP、そしてπ計算


erlang-scala-go 現在 ErlangScala といった言語の最近の成功に牽引されているアクター並列モデルは、探索する価値のある「代替となる並列モデル」の良い例だ。しかし、これしかないというわけではない。
Google の Go 言語もまた関連する、しかしとても異なるモデルを持ち込んだ: トニー・ホーアのCSPπ計算のミックスだ。表面上では、上記の言語群に採用されたモデルはお互いに非常に異なったものだが、しかし全ては共通の中核原則に基づいている:


「状態を共有することによってコミュニケーションするな。
 コミュニケーションによって状態を共有するようにせよ」


少しの間、沈思してみよう。データ構造をロックによって守りそのロックを奪い合う代わりに、このモデルは明示的にプロセスからプロセスへ状態を渡すこと推奨する。これは、ある時点ではただ一つだけのプロセスがデータにアクセスできることを保証し、そして即座に並列性問題の全体を解決する。

アクター、Goroutine、Channnel と Ruby


 それで、類似性はわかったが、Erlang と Go のような言語の間の実際の違いは何なんだろう? 構文やVM実装の詳細はさておき、Erlang では 各プロセスに独自に与えられた名前を使ってコミュニケーションをとる――mailbox という考え方だ。対照的に Go では、全てのプロセスは無名であり、その代わりに "communication channel" を指定する――Unixのパイプの考え方だ。微妙な違いだが、これらが非常に異なるプログラミングモデルと能力をもたらす。

erlang-vs-go

理論的なことはわきにどけて、Go の並列モデルを注意深く見てみよう。Go runtime をインストールするかもしくは、実は Ruby ランタイム上で直接そのモデルのほとんどをエミュレートすることもできる(gem install agent)。:
c = Agent::Channel.new(name: 'incr', type: Integer)
 
go(c) do |c, i=0|
  loop { c << i+= 1 }
end
 
p c.receive # => 1
p c.receive # => 2
githubAgent (Go-like concurrency, in Ruby)
Downloads: 275 File Size: 0.0 KB

シンプルなジェネレータの例だが、多くの異なる機能をハイライトさせるものでもある。まず最初に、プロセス間でコミュニケーションを取るための名前('incr')のついた型付き channel を作成する。それから少し面白いことに "go" という、channel を一つと Ruby のコードブロックを取る関数を呼ぶ。

裏側では、"go" 関数は実際にはコードブロック(ここでは loop 命令)を実行するための新しいスレッドを作成し、元の channel で receive を呼ぶためにすぐさま復帰する。その channel は、今度は、producer が値を生成するまで receive 呼び出しをブロックする! 言い換えれば、マルチスレッドな producer-consumer を、シングルスレッドまたは mutex が見えないように書いたってことになる! また、よりおもしろいマルチワーカーの例エラトステネスのふるい、そして MRI と JRuby の間の非科学的な小競り合いの結果を見よ。

"先進的な並列性" の受け入れ


 シェアードメモリ、スレッドそしてロックにはそれらを使う場所と目的がある。実際、上で紹介した Agent の裏側か、"代替となる並列モデル" を持つランタイムのソースコードの中を見てみれば、間違いなくそれらが使われているのが見つかるだろう。だから、スレッドの存在が必要かどうかは疑問点ではなくむしろ、重要なのはランタイムに関わらず、並列性を必要とするコードを書き、テストし、管理するベストで高水準なインターフェースが実際になされているか、だ。

アクターや CSP/π計算モデルは最初は複雑に見えるが、ほとんどはそれに馴染みないせいだ。実際、一度いくつか例を経験してみれば、それらは著しくシンプルで、パワフルで、そしてどんなランタイムの中でも再現できる。Erlang を始めるか、Go を試してみるか、あるいは Ruby でアイデアをプロトタイピングするために Agent をインストールするかすれば、あなたは時間を有益に過ごせるだろう。


0 件のコメント:

コメントを投稿

フォロワー