Rubyコーディングあれこれ (初めての方むけ: 並列 Parallel.eachの使い方)
本地震の予測マップ・プログラムの開発言語は Ruby なのですが、以前から初めての方むけにコーディングテクをまとめておければ、と考えていました。 そこで思い付くままにまとめてみよう、と考えております。
今回は、並列制御の Paralle.each を使う場合のコメントです。 (短くまとめます)
私は以前スレッド並列を実装して使っていました。 未だ Parallel.each 出てくる前の事です。 ノートパソコン上4スレッド並列で1.5倍くらい、サーバ上8スレッド並列で2.5倍くらいの高速化でした。 (アプリは外部ファイルアクセスを多用するアプリでした。)
今回、地震の予測マップ・プログラムは割と早い段階で Parallel.each を in_processes:4 にてトライしています。 結果はノートパソコンで25%程度の高速化でした。 それほど高速化できない原因は分かっているつもりですが、まずは Parallel.each の使い方(現在、私は使っていませんが)について、私の分かる範囲でコメントを述べさせて下さい。
まず Parraliel ですが gem install parallel でインストールする必要があります。 その後、 require "parallel" を指定する必要があります。
その上で、hash_in に処理したい各key名とデータ列(入力データ)を作っておき、 hash_out なる処理結果を受け取る出力用ハッシュを宣言しておき、
keys_array = hash_in.keys # まず処理したい key 列を作っておいて、
Parallel.each(keys_array, in_processes:4) do |key| # key をスキャンさせて、
hash_out[key] = application(hash_in, key) # アプリは hash_in と key を受け取って、
# ^^^^^^^^^^^ 出力ハッシュの同じ key 名に結果を戻します。
end
とこのようにすべてハッシュの key の下で管理します。 パラレルはハッシュがないと動かない、と言われる所以です。
application 下位階層のルーティン内ローカルデータもすべてハッシュで管理します。 特に中間出力となるデータは必ず key で管理されたハッシュです。 入力の場合も私はすべてハッシュで管理しましたが、そこまでの必要はなかったのかもしれません。
application 下位階層間のインターフェイス(引数)ですが、必ずシンボルかハッシュで受け渡します。 ストリングは駄目です。 nil を引数に書く場合がありますが、これも(絶対)駄目です。
以前私がやった並列アプリは外部ファイルを多用するものでしたのでスレッド並列でOKでしたが、今回の地震の予測マップ・プログラムはメモリ処理主体ですので in_processes:4 でトライした訳です。 ( in_ptosesses は Linux コマンドによる fork 並列と同じだそうですので。)
結果が25%しか早くならなかったのは私のノートはキャッシュメモリが少なすぎて(非力で)メモリアクセスネックとなったからだ、と思っています。 大きなサーバでしたらもう少し早くなると思いますが、元々私のオペレーションはノート主体(というか、それしか無い)ですし、25%ではどうしようもなくかつ今後すべてをハッシュ管理してゆくのは重たく感ぜられて、 Parallel.each は止めています。
ですが、キャッシュメモリが十分なサーバが在るようでしたら Parallel.each は、ハッシュ化は必要ですが、割と簡単に並列化できるので、是非使ってみるべき機能である、と思っています。 昨今、プロセッサは余り気味で使ってくれと叫んでいるでしょうから。
以上です。