PeerCastIM4Linuxをいじってみた
PeerCast には設定の為に HTML サーバーが実装されているが、そのパフォーマンスはさして感心するようなものでもない。Apache HTTP サーバーのベンチマークツールである ab で計測してみる((ab -c 1 -n 1000 http://localhost:7144/html/ja/images/small-logo.png
等 ))と、コンカレンシー 1〜4 の時のリクエスト毎秒は次の通り。
Requests per second: 94.13 [#/sec] (mean) Requests per second: 93.90 [#/sec] (mean) Requests per second: 94.16 [#/sec] (mean) Requests per second: 94.72 [#/sec] (mean)
どうもなんらかの天井があるようだ。
ソースを見ると、ポートで接続待機するスレッドのエントリー関数の概要は次のようなものだった。
int Servent::serverProc(ThreadInfo *thread) { Servent *sv = (Servent*)thread->data; // 待機状態にする。 sv->setStatus(S_LISTENING); while ((thread->active) && (sv->sock->active())) { ClientSocket *cs = sv->sock->accept(); if (cs) { 要求を処理するために新しいスレッドを起動する。 } sys->sleep(10); } 終了処理。 return 0; }
accept はノンブロッキングであるので、接続要求があれば ClientSocket オブジェクトを返すが、なければ NULL を返す。要求のあるなしに関わらず 10 ms のスリープを行なうことで CPU 時間の浪費を防いでいる仕組みだ。
1秒 = 1000 msec なので、このやりかたではどうがんばっても1秒間に 100 回以上のリクエストに応えることはできない。およそ 94 リクエスト毎秒だったこともうなずける。
接続要求が大量に来た場合はシステムのキューに要求が溜まっているはずなので、accept が連続して成功する場合が多い。そこで、accept が成功した場合は sleep せずに次の accept をこころみるように変更してみる。
while ループを次のように変更した。
while ((thread->active) && (sv->sock->active())) { ClientSocket *cs = sv->sock->accept(); if (!cs) { sys->sleep(10); continue; } 要求を処理するために新しいスレッドを起動する。 }
コンカレンシー 1〜4 の時の RPS。
Requests per second: 94.01 [#/sec] (mean) Requests per second: 182.73 [#/sec] (mean) Requests per second: 279.56 [#/sec] (mean) Requests per second: 363.40 [#/sec] (mean)
コンカレンシーを上げると線形に RPS も増加するようになった。
さらに、Unix 依存にはなるが*1 poll を使っていつでも要求が来たらすぐさま accept できるように改造してみた。
#include <poll.h> #include "../unix/usocket.h" while ((thread->active) && (sv->sock->active())) { { struct pollfd fd; fd.fd = ((UClientSocket *) sv->sock)->sockNum; fd.events = POLLIN; fd.revents = 0; int changed = poll(&fd, 1, 10); // 10 msec のタイムアウト。 if (changed == 0) { // タイムアウトが起こった場合。 continue; } } ClientSocket *cs = sv->sock->accept(); if (!cs) continue; // 恐らく到達しない。 要求を処理するために新しいスレッドを起動する。 }
コンカレンシー 1〜4 の時の RPS。
Requests per second: 1743.66 [#/sec] (mean) Requests per second: 2382.99 [#/sec] (mean) Requests per second: 2009.52 [#/sec] (mean) Requests per second: 1775.24 [#/sec] (mean)
なぜコンカレンシーに応じて RPS がこのように変化するのかはわからないが、ともあれ大幅に改善した。
*1:でもまあ PeerCastIM4Linux だし :-)