読者です 読者をやめる 読者になる 読者になる

selectよりkqueue,epoll(apache2のススメ)

最近3人ほどのエンジニアと話したのだがapache2に対して割とネガティブな意見を持っていた.

曰く「既存モジュールが使えないから」
曰く「スレッドベースってちょっと。。」
曰く「WEBでいい話聞かないから」


3人しか話してないんだけど,3人とも「apache2はスレッドでしか動かない」と思いこんでたようでちょっとおどろいた.apache2でも


    StartServers          5
    MinSpareServers       5
    MaxSpareServers      64
    MaxClients          100
    MaxRequestsPerChild 10000

という設定をすることで今までどおりpreforkモデルで動かすことはできる.preforkモデルだと各種ハンドラもスレッドセーフに無理にすることはないので,わかってて使う分には問題ない.


私がapache2を勧める1番の理由はapache2ではリクエストの多重化処理を従来のselectからkqueueベース(Linuxではepoll)にしているというところだ.squid2.6のCOSSの話(その2)にも書いたとおり,apache1.3の処理におけるselectの割合は比較的高く,大量にHTTPアクセスを受けているサーバにとってそのコストは非常に大きい.なのでいかにselectが遅く,そしてkqueueが効率いいかを知ってもらいたいと思う.


select(2)を使ったサーバプログラムのソケット処理部は大体こんな感じになっている.

while(1){
  if (ソケットにパケットが届いてたら){
    リクエスト処理
  }
}

ここでの「ソケットにパケットが届いてたら」を調べるのがselect(2)なわけだが,こいつは調べたいソケットをループで全部調べるという実装になっているため,オープンしているソケットが多いほど遅くなる(もうちょっと正確には全部じゃなくて対象ソケットのうち1024個分を事前に登録しといてそれを調べる).これはHTTPアクセスが非常に多いWebサーバでは結構致命的な実装で,さらに悪いことにソケットの情報はカーネルメモリにしかないのでシステムコールを呼ばざるを得なく,これまた遅い.また上記のwhile文は基本無限ループになっていて,たとえ今ソケットに読み出すべきデータがないとしてもselectを呼び出す結果により,さらに遅さに拍車をかける.


そこで開発されたのがkqueueだ.kqueueはファイルハンドルに対して起きる各種イベント(消されたとかWriteされたとか)に対して実行したい関数ポインタを登録しておく機構だ.この方式だとカーネル側からイベントを通知してもらえるので,selectと違って毎回無駄なループを走らせる必要のない分速い.


これはデザインパターンでいうところの「Observerパターン」なので,興味がある人はそちらからあたるのもいいだろうと思う.


ざっとだがselectが遅い(そしてkqueueが効率いい)のは理解してもらえたと思う.ちなみにlighttpdがapache1.3に比べて速い大きな理由のひとつがこれで,用途によるけれどapache1.3からapache2.Xに変更するだけで3割以上パフォーマンスがアップすることもあるので,興味ある人はぜひとも試してもらいたい.

(おしまい)

追記2006/11/08

もうちょっと正確にはselect(2)が遅いというよりselectを使ったプログラミングモデルが遅いというのが正確なところだろう.select自体もそこそこ遅いが,whileループをまわすことにより無駄なチェックをさせてること自体が遅い原因になっている.

yamaz的日常

19×19の掛け算の覚え方の本を読んだ.12から19までの数字に8人の登場人物を割り当て,その組み合わせにストーリーをつけることによって19×19の掛け算が簡単に覚えられるように工夫されててなかなか感心した.
16*16=256はプログラマなので当然知ってましたが,15*17=255というのは今まで気づいてませんでしたよ..


なおインドでは小学生の段階で20×22の掛け算ができるようになってるらしいので,日本人も負けないようにがんばっていきたい.

19×19 トクトク―日本人のアタマをもっとよくする2桁かけ算

19×19 トクトク―日本人のアタマをもっとよくする2桁かけ算

あわせて覚えておきたい 「2^10(2の10乗)は1024」

普通の人にとって1024は一般的な数字じゃないので,
「2^10はだいたい1000」の方が日常的には役に立つでしょう.
あと1000×1000=100万,1K(1024)×1K(1024)=1Mとか.