第20回 アクターで楽々並行プログラミング(2)
小笠原 啓
有限会社ITプランニング勤務のプログラマー。Scala、OCaml、F#などの静的型付け関数型言語を利用したシステム開発業務に従事。定理証明支援器Coqやモデル発見器Alloyといった形式手法ツールの業務への応用にも興味を持っている。
前回は、アクターとはメッセージ受信ループを内包した並行に動く計算主体であること、Scalaにはアクタースタイルの並行プログラミングをサポートする基本機能やタイムアウト、Futureなどの便利な機能が備わっていることをご紹介しました。 今回は、アクターを実戦に投入するに当たって知っておきたい、パフォーマンスに関する議論について解説します。やや詳細に踏み込んでいきますが、「アクターには飽きたー」などと言わず、最後までお付き合いください。 なお、本稿ではScala Version 2.8.0を前提とします。Version 2.7.7のアクター用タスクスケジューラーは本稿の説明とは若干異なる動きをしますので、旧バージョンでの動作に興味がある方は、本稿最後のカコミ記事をご覧ください。 アクターとスレッドの関係アクターの並列性は、JavaVMのスレッドを使って実装されています。このことはロックフリーなアクタースタイルでプログラムを組む上では特に考える必要がなく、むしろその下位構造を意識しなくてもいい高度な抽象化がアクターの強力さの源泉でもあります。 しかし、実際のプログラム開発においてアクターを使うとなると、その実行パフォーマンスやリソース消費について知らない訳にはいきません。 例えば、アクターとスレッドは一対一に対応するのでしょうか? そうだとすると、アクターは気軽に生成できない高コストなオブジェクトであり、アプリケーション全体で利用するアクターの数を気にする必要がでてきます。 そこで、リスト1のような簡単な実験をしてみます。
リスト1●250個のアクターを動作させるコード
リスト1では、Showメッセージを受け取ったら、カレントスレッドのID番号と名前を表示して終わるアクターを250個作り、そのそれぞれにShowメッセージを送っています。もしアクターとスレッドが一対一対応するなら、このプログラムを実行すると、250個のスレッドIDが画面に羅列されるはずです。 リスト1実行結果は次のようになります。
私の端末環境では、4種類のスレッドIDと名前が表示されました。つまり、250個のアクターを動作させたにもかかわらず、実際にアクターに割り当てられたスレッドは4種類だったということになります。実行環境によっては4種類だけではなく、もっと多くの種類のスレッドIDと名前が表示されることもありますが、250個よりは少ないはずです。 表示されたスレッドの名前からも想像できますが、実はScalaのアクターライブラリは環境に合わせた個数(最低でも、4個もしくはCPU数*2個のうち大きい方)のワーカースレッドを自動的に作成し、複数のアクターの実行のために少数のワーカースレッドを使い回します。 そもそもreceive関数によってメッセージ待ちになったアクターは、スレッドを占有している必要はありません。次回メッセージが届いたら動き出せばいいのですから、その間は別の処理をしていても構わないはずです。 Scalaのアクターライブラリにはこのようなスレッドの使い回しの仕組みがあり、利用する側としては多くのアクターを生成してもスレッドリソースの無駄遣いを気にしなくてもよいようになっています。 なお、動的に生成されるスレッドの個数の上限は、システムプロパティ actors.maxPoolSize で設定することができ、この値はデフォルトでは 256 になっています。もし256個以上の並列性が必要な場合には、この値を増やしておきましょう。
>>タスクスケジューラーの詳細
連載新着連載目次へ >>
|