RubyのThread::SizedQueueのチューニング
前提
Thread::Queue
は際限なくサイズが伸びるので使わない。Thread::SizedQueue
を使う。- 生産者(
Thread::SizedQueue#push
する)側は1スレッドとする。 - 消費者(
Thread::SizedQueue#pop
する)側は複数スレッドとする。
結論
いろいろい試してみたが、次のようにすればよさそう。
消費者(popする)側が高速に処理できるとき
スループットを最大化するためには、Thread::SizedQueue
のサイズを目安として消費者側のスレッド数の10倍程度に設定する。
消費者(popする)側が低速にしか処理できないとき
Thread::SizedQueue
のサイズをいくら大きくしたところで無駄に滞留するだけなので、なるべく小さなサイズにする。極論としてはサイズ1でもかまわない。
理由
Rubyの条件変数(Thread::ConditionVariable
)は、wait
メソッドで同時に待ち状態になるスレッドの数が多くなるほど加速度的に遅くなるようだ。signal
メソッドで通知先を1スレッドに限定しても速度低下は緩和されない。
Thread::SizedQueue
は組み込みライブラリなので厳密には違うのだが、スレッド待機の実装はThread::ConditionVariable
とそんなに変わらないと思われる。
速度低下を避けるためにはwait
の呼び出しをなるべく減らさなければならず、Thread::SizedQueue
が空だとpop
するときにwait
するのでキューにデータが入っている状態を保たなければならず、そのためにはThread::SizedQueue
のサイズをpop
するスレッド数より十分大きくする必要がある。
ちなみにpush
する側でもwait
は発生するが、1スレッドに限定されるなら速度低下は気にするほどではない。