コミケ告知

サークル networkmaniacs.net(旧:浜風もっこす) 2017.02.17 デブサミ内 DevBooksに出展します。
詳細は circle タグの記事へ。
2014年10月11日土曜日

Akkaの挙動確認用の小物を作った

AkkaのActorスケジューリング周りの挙動を確認するための、ちょっとしたものを作りました。

概要

Actorの数、OSスレッドの数、Actor1個あたりに飛ばすメッセージ数を指定して回し、簡単なレポートを出力します。Actorのメッセージハンドリングが、どのスレッドでどんな順番で実行されたのかが表示されます。
適当に時間がかかるように、Actorはメッセージを受け取るごとにフィボナッチ数を求めています。この実験では別にsleepでも良いんですが、後々試したいことがいくつかあることと、OSやVMによるスレッドのスケジューリングに手を出したくないことから、この方法にしました。

Akkaの設定

参考:Configuration — Akka Documentation

スレッド数の設定

スレッド数は固定値を指定するものではなく「factor」「parallelism-min」「parallelism-max」の3項目と、実行環境で認識されたプロセッサー数によって決定します。まず最初に使われるのがfactorで、(プロセッサー数 * factor)を切り上げた値が用いられます。その後、parallelism-minparallelism-maxの指定範囲内にない場合は、指定範囲に収まるように値が補正されます。minよりmaxの方が小さい場合、maxが優先されるようです。

あくまでもfactorで計算された値が基本であるため、max設定値だけ大きくしてもスレッドは増えません
このテストプログラムでは、引数-apf, -apmin, -apmaxによって、この3項目を設定可能です。

スループットの指定

Actorがスレッドを手放すまでに処理するメッセージの数を設定するのが、akkaのthroughputです。パッと見では、用語から挙動を想像するのが難しいような気がします。

AkkaのActorスケジューリングに用いられる単位は、メッセージです。Actorが(receiveで)メッセージを受け取ると、どこかのスレッドに割り当てられ、receiveのブロックから抜けると、Akkaのシステム側に制御を委ねます。以下の例では、Msg1を受信したらdoFoo()を実行して終了、Msg2を受信したらdoBar1()とdoBar2()を実行して終了。
class HogeActor extends Actor {
  override def receive = {
    case Msg1 =>
      doFoo()
    case Msg2 =>
      doBar1()
      doBar2()
  }
}
スレッドを明け渡すまでに、メッセージをいくつ連続で処理するかを示す設定値がthroughputです。メッセージがActorのメールボックスに5個届いていたとして、throughputが5以上あれば、一気に全部処理します。throughputが1であれば、1個処理したところで、Akkaのシステムに一度制御を返します。他にメッセージ処理を待っているActorがいれば、そちらにスレッドを譲ることになります。
スレッド上で走るActorの入れ替えにはコストがかかるので、一気に走り続けた方が性能面では有利です。しかし、あるActorが居座ったままだと、他のActorがいつまで経っても処理を始められないかもしれません。Configuration説明ページのコメントに set to 1 for as fair as possible とあるように、このパラメーターはthroughputとfairnessのトレードオフです。ここまで説明してようやく、パラメーター名の意味がわかります。
throughputパラメーターは、冒頭のテストプログラムでは -at で設定できます。メッセージの数を多めに、スレッドの数をActorの数より少なく設定すると、何がどうなるのか観察できます。

補足

各WorkerActorの始動に1つメッセージを消費しているため、最初の1サイクルはメッセージ1個分ずれます。

0 件のコメント:

コメントを投稿