PeerCast には設定の為に HTML サーバーが実装されているが、そのパフォーマンスはさして感心するようなものでもない。Apache HTTP サーバーのベンチマークツールである ab で計測してみる1と、コンカレンシー 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 依存にはなるが2 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. ab -c 1 -n 1000 http://localhost:7144/html/ja/images/small-logo.png等 

  2. でもまあ PeerCastIM4Linux だし :-)