Esehttpdのパフォーマンスチューニング


戻る

Esehttpdの性能

esehttpdはrealtime signalsとnonblocking IOを利用した高性能・スケーラブルなIO多重化方式に基づくサーバです。この方式は、apacheのようなpre-fork型サーバやマルチスレッド型サーバよりもリソース消費や様々なオーバヘッドが少なく、TUXなどのカーネル内サーバに匹敵する性能が得られます。またselect/poll型サーバと比較すると、同時接続数が増加してもサーバ全体の性能が低下しないという利点があります。さらにesehttpdは、HTTP 1.1のパイプライン化されたリクエストに対して、複数のレスポンスを一つのパケットで返せるようにしてあります。よって小さなレスポンスを大量に返すようなケースで非常に高い性能が出ます。

ただし、それほどアクセス数の多くないサイトではapacheと比較して大幅に性能が上がるということはなく、CPUの使用率やメモリの消費が減る程度だと思われます。これは、よほど外部へのネットワークが高速でない限りそのネットワークのバンド幅を満たすのにはapacheの性能で十分だからです。逆にCPU使用率が上がって遅くなるケースのほとんどは、CGIスクリプト等の動的コンテンツの生成が原因で、これはesehttpdでの性能の向上はわずかです(Cで書かれたCGIでも高々2倍程度で、rubyやperl等のスクリプト言語だとその差はもっと縮まります)。

esehttpdが最も性能を発揮するのは、LAN等の高速なネットワークでクライアントと繋がっているか、同時に大量のクライアントと接続する必要があるか、CPUやメモリの資源に乏しいといったケースです。それらの場合、以下に示すポイントを参考にして設定をおこなえば、かなりの性能が期待できるでしょう。

アクセスログを使わない

あまりにリクエストが大量に来る場合は、クライアントとサーバ間の通信よりもむしろアクセスログのディスクへの書き込みがボトルネックになることがあります。その場合、もしアクセスログが必要無いならばCustomLogディレクティブをコメントアウトすればアクセスログが出力されなくなります。アクセスログはバーチャルホスト単位で止めることが出来ますが、サーバ全体で完全にアクセスログを止めるとさらに少しだけ性能が向上します。

ファイルキャッシュを大きくする

FileCacheSizeディレクティブに大きな値を指定して、Webサーバでサービスする全てのファイルがファイルキャッシュに収まるようにしてしまいます。FileCacheSizeに大きな値を指定する場合は、OSが扱えるファイルの数をこの値よりも大きくしなければなりません。たとえばLinuxでは/proc/sys/fs/file-max/proc/sys/fs/inode-maxを書き換える必要があります。なおファイルキャッシュを大きくするとesehttpdのメモリ使用量が見かけ上大きくなりますが、これはファイルキャッシュに収まったファイルがすべてesehttpdのメモリ空間にマップされるためで、実際に増加する物理的なメモリ使用量はわずかです。

プロセスが扱えるファイル数を増やす

esehttpdは一つのプロセスで複数のクライアントとの接続を取り扱うため、プロセスあたりに利用できるファイル数が少ないと同時接続数も制限されてしまいます。それを避けるには、MaxFilesに大きな値を指定します。これに指定した値がほぼ、最大の同時接続数になります。これに指定した値とFileCacheSizeの合計が、OSが扱えるファイルの数をじゅうぶん下回るように注意して下さい。

SMP構成ではマルチプロセス起動する

CPUが2個以上のシステムでは、StartServersにCPU数以上の値を指定して下さい。

ネームバーチャルホストを避ける

ネームバーチャルホストはホスト名を確定するためにハッシュテーブルを引くので、わずかですが遅くなります。ほとんど測定不能な程度ではありますが、使わないに越したことはありません。

AddTypeやAddHandlerを使わない

これらを使うとリクエスト毎にハッシュテーブルを引く回数が一つ増えます。

<Directory>をなるべく使わない

<Directory>を一つでも使うとハッシュテーブルを引くので、わずかに遅くなります。

MultipleAccept

realtime signals方式ではMultipleAcceptをOffにしたほうが高速です。poll方式では大抵の場合Onにしたほうが高速です。

<FilesMatch>をなるべく使わない

これもほとんど測定不能な程度ですが、わずかに遅くなります。また、パターンにマッチするか否かの判定にかかるコストは、<FilesMatch>セクションの個数について線型に増加しますので、あまり数を増やすとそれだけ遅くなります。

Linux 2.4系を使う

2.2系とでは性能がかなり違います。また2.4以上でないとrealtime signals方式が利用できません(esehttpdを2.4カーネルを使ってコンパイルしないと有効になりません)。さらに2.4カーネルにesehttpdソースに付属するkernel-pachesディレクトリ以下にあるパッチを当てると、わずかですがesehttpdの性能が上がります。

NDEBUGを定義してコンパイルする

esehttpdのソースはassert()を大量に使っていますので、CFLAGS="-O3 -DNDEBUG" ./configureのようにしてビルドすると少し速くなります。

補足: ベンチマークを取る際の注意

Webサーバの性能を見るためにベンチマークを取る際には、いくつか注意をしなければならない点があります。最も重要なのは、テストを律速しているのがどの部分であるかを見極めることです。例えばFastEtherのネットワークでサーバからクライアントにHTTP GETを繰り返すテストをおこなう場合、律速する可能性のありそうな箇所は次のようになります。
ネットワークのバンド幅
サーバからクライアントへのレスポンスがネットワークのバンド幅を埋め尽くす場合。GETするファイルのサイズが大きいときにはほとんどがこのケースになります。転送速度がFastEtherの限界にほぼ一致する場合はこのケースと考えていいでしょう。その場合転送速度とWebサーバの性能は全く関係なくなってしまうので、せいぜいWebサーバのCPU使用率を見て性能を比較する程度しかテストの意味がありません。
サーバのCPU
サーバのCPU使用率が100%になった場合。この場合は転送速度がWebサーバの性能を反映しているといえます。毎秒のリクエスト数あるいは毎秒の接続数でWebサーバの性能を測ることができます。
クライアントのCPU
Webサーバが処理できるリクエストの量のほうがクライアントが処理できる量より多くなった場合にこのようになります。apacheではこのようなことは起きませんが、esehttpdなどのサーバではよく起きます。これが起きるとクライアントのCPU使用率が100%になり、サーバ側のCPU使用率は100%より低くなります。一部のベンチマーク用プログラム(例えばhttperf)ではイベントループがポーリングするようになっていて、たとえクライアント側の処理能力に余裕があってもCPU使用率が100%になるものがあるので注意が必要です。
最も性能を測りやすいのはサーバのCPUが律速するケースですが、高速なWebサーバでこの状況にするには、サーバマシンに敢えて低速なCPUを使うか、あるいはクライアントを複数台使ってテストする必要があるかもしれません。

何を比較するべきか?

Webサーバの性能をテストするうえで重要な項目といえるのは、単位時間あたりに処理できるリクエスト数、単位時間あたりに処理できる接続数、可能な最大の同時接続数、同時接続数の増加に伴う速度低下の度合、動的コンテンツ生成のオーバヘッド、などが挙げられます。

単位時間あたりに処理できるリクエスト数(requests per sec, RPS)がWebサーバの「生の」性能を最も反映している項目です。HTTP 1.0当初は1つの接続あたり1つのリクエストとレスポンスしか処理できなかったのですが、その後「Connection: keep-alive」ヘッダを付加することによって1つの接続で複数のリクエストを処理するような拡張が使われるようになり、HTTP 1.1ではそれをさらに拡張して「パイプライン化されたリクエストとレスポンス」が使えるようになりました。HTTPクライアントとサーバの両方がこれらの機能に対応しているかどうかで、RPSの値は大きく変わってきます。よってサーバとクライアント間で、 のいずれのタイプで通信しているかを把握してテストする必要があります。

単位時間あたりに処理できる接続数(connections per sec)の値が重要になるのは主にHTTP 1.0クライアントとの通信です。最近のWebブラウザのほとんどはHTTP 1 1に対応していますが、Webブラウザ以外のHTTPクライアント(たとえばロボットなど)は対応していないものも多くあります。またHTTP 1.1対応のクライアントでも、1つの接続であまり多くのリクエストを送らない場合には単位時間あたりに処理できる接続数の値が性能に響いてきます。

HTTPサーバのIO多重化方式によっては、同時に多くのクライアントがそのサーバに接続しているだけで性能が落ちることがあります。たとえば、500個のクライアントがHTTPサーバに接続して何も通信しない状態でさらに1つのクライアントがHTTPサーバと通信して性能を測定した場合、これら500個のクライアントをHTTPサーバに接続しないで同様の測定をした場合と比較して大幅に性能が下がります。この現象がおこるのはpre-fork型やselect/poll型のIO多重化をつかったサーバで、esehttpdが使っているrealtime signals方式やTUXのようにカーネル内部に組み込んだ場合には起きません。インターネット上ではネット的に遠くのホストから接続することが多く、必然的にクライアントが長い時間何も通信しない状態になりやすく同時接続数が増えるため、この現象が起きないことが重要になってきます。

HTTP 1.1パフォーマンス

上の図は、HTTP 1.1のパイプライン化したリクエストを送ったときのサーバの性能を、接続あたりのリクエスト数を変化させて測定した結果です。GETするファイルのサイズが大きいとネットワークのバンド幅が律速してしまうので、このテストでは非常に小さいファイル(2バイト)を転送して1秒あたりに処理できるリクエスト数を測定しています。接続あたりのリクエスト数が増えればそれだけ処理できるリクエスト数が上がりますが、これは接続を開いたり閉じたりする処理が減るためと、複数のリクエストをパイプライン化する効果によるものです。接続あたりのリクエスト数が1の場合の結果が、ほぼHTTP 1.0での性能に一致します。このテストはOSはLinux-2.4.2にTUXが組み込まれたカーネル、サーバがPentium3 700MHz、クライアントには4台使い、それぞれのクライアントから4接続を並行に繋いで測定しました。

参考: Esehttpdが利用している高速化の技法について

esehttpdは以下の技法を用いて、なるべく高速かつ資源を浪費しないように作ってあります。
POSIX realtime signalsによるIO多重化
esehttpdは一つのプロセスで複数のクライアントと同時に通信するため、リソースの消費が少なく、またIO多重化のオーバヘッドも少なくなっています。realtime signalsによるIO多重化を使うことによって、select()やpoll()のスケーラビリティの問題を回避しています。
ファイルキャッシュ
静的コンテンツの配信のためのキャッシュを持っていて、ディスク上のファイルはこのキャッシュにmmapされます。レスポンスヘッダもそのキャッシュに入るため、リクエストのたびにヘッダを作り直すことはありません。ディスク上のファイルが更新されればキャッシュのエントリも破棄されます。Pragma: no-cacheなどが付いたリクエストがあった場合には常にファイルの更新をチェックします。
keep alive
HTTP 1.0のkeep aliveとHTTP 1.1のpersistent connectionに対応しています。ただしHTTP 1.0のkeep aliveリクエストに対してはファイルキャッシュが利用できないため、それほど速くはならない場合があります。
レスポンスのパイプライン化
HTTP 1.1のパイプライン化されたリクエストに対する複数のレスポンスを一つのパケットで返すことができます。パケットの数が減るためネットワークへの負荷が小さくなり、また反応速度も向上します。304レスポンスなどのように、小さなレスポンスを大量に返す場合に特に有効です。
If-Modified-Since
文字列の比較による単純な方法によってIf-Modified-Sinceに対応しています。
Rangeリクエスト
Rangeリクエストに対応しています。ファイルの一部分だけを返すことができます。ただしmultiple rangesはあまり使われることがないため、対応していません。

戻る
Akira Higuchi