Esehttpdのパフォーマンスチューニング
戻る
Esehttpdの性能
esehttpdはrealtime signalsとnonblocking IOによるIO多重化方式を利用しています。この方式は、apacheのようなpre-fork型サーバやマルチスレッド型サーバよりもリソース消費や様々なオーバヘッドが少なく、カーネル内サーバにほぼ匹敵する性能が得られます。また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カーネルを使ったシステム上でコンパイルしないとrealtime signalsは有効になりません)。
NDEBUGを定義してコンパイルする
esehttpdのソースはassert()を大量に使っていますので、CFLAGS="-O3 -DNDEBUG" ./configure
のようにしてビルドすると少し速くなります。
補足: ベンチマークを取る際の注意
Webサーバの性能を見るためにベンチマークを取る際には、いくつか注意をしなければならない点があります。最も重要なのは、テストを律速しているのがどの部分であるかを見極めることです。例えばFastEtherのネットワークでサーバからクライアントにHTTP GETを繰り返すテストをおこなう場合、律速する可能性のありそうな箇所は次のようになります。
- ネットワークのバンド幅
- サーバからクライアントへのレスポンスがネットワークのバンド幅を埋め尽くす場合。GETするファイルのサイズが大きいときにはほとんどがこのケースになります。転送速度がFastEtherの限界にほぼ一致する場合はこのケースと考えられます。その場合、転送速度とWebサーバの性能は全く関係なくなってしまうため、テスト結果からWebサーバの性能を判断することはできません。
- サーバのCPU
- サーバのCPU使用率が100%になった場合。この場合は転送速度がWebサーバの性能を反映しているといえます。毎秒のリクエスト数あるいは毎秒の接続数でWebサーバの性能を測ることができます。
- クライアントのCPU
- Webサーバが処理できるリクエストの量のほうがクライアントが処理できる量より多くなった場合にこのようになります。これが起きるとクライアントのCPU使用率が100%になり、サーバ側のCPU使用率は100%より低くなります。ただし一部のベンチマーク用プログラム(例えばhttperf)ではイベントループがポーリングするようになっていて、たとえクライアント側の処理能力に余裕があってもCPU使用率が100%になるものがあるので注意が必要です。その場合、サーバ側のCPU使用率が100%なのであればそのテスト結果はサーバの性能を反映しています。
サーバのCPUが律速するようにするには、サーバ側に低速なCPUを使うか、あるいはクライアントを複数台使ってテストする必要があるかもしれません。
性能テストする項目
Webサーバの性能で重要といえるのは、単位時間あたりに処理できるリクエスト数、単位時間あたりに処理できる接続数、可能な最大の同時接続数、同時接続数の増加に伴う速度低下、同時処理の増加に伴う速度低下、動的コンテンツ生成のオーバヘッドなどです。
単位時間あたりに処理できるリクエスト数(requests per sec, RPS)がWebサーバの静的コンテンツに関する性能を最も反映します。HTTP 1.0では基本的に1つの接続あたり1つのリクエストとレスポンスしか処理できませんが、Connectionヘッダを付加することによって1つの接続で複数のリクエストを処理するような拡張が広く使われています。HTTP 1.1ではそれをさらに拡張して「パイプライン化されたリクエストとレスポンス」が使えます。HTTPクライアントとサーバの両方がこれらの機能に対応しているかどうかで、RPSの値は大きく変わってきます。そのためサーバとクライアント間で、
- HTTP 1.0
- HTTP 1.0 + keep-alive
- HTTP 1.1
のいずれで通信しているかを把握してテストする必要があります。
単位時間あたりに処理できる接続数(connections per sec)の値が重要になるのは主にHTTP 1.0クライアントとの通信です。最近のWebブラウザのほとんどはHTTP 1 1に対応していますが、Webブラウザ以外のHTTPクライアント(たとえばロボットなど)は対応していないものも多くあります。またHTTP 1.1対応のクライアントでも、1つの接続であまり多くのリクエストを送らない場合には単位時間あたりに処理できる接続数の値が性能に影響します。

上の図は、HTTP 1.1のパイプライン化したリクエストを送ったときのサーバの性能を、接続あたりのリクエスト数を変化させて測定した結果です。GETするファイルのサイズが大きいとネットワークのバンド幅が律速してしまうので、このテストでは非常に小さいファイル(2バイト)を転送して1秒あたりに処理できるリクエスト数を測定しています。接続あたりのリクエスト数が増えればそれだけ処理できるリクエスト数が上がりますが、これは接続を開いたり閉じたりする処理が減るためと、複数のリクエストをパイプライン化する効果によるものです。接続あたりのリクエスト数が1の場合の結果が、ほぼHTTP 1.0での性能に一致します。このテストはOSはLinux-2.4.2にTUXが組み込まれたカーネル、サーバがPentium3 700MHz、クライアントには4台使い、それぞれのクライアントから4接続を並行に繋いで測定しています。
参考: 高速化に関連する拡張の対応等について
- 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はあまり使われることがないため、対応していません。
- ファイルキャッシュ
- Esehttpdは内部に静的コンテンツのためのキャッシュを持っていて、ディスク上のファイルはこのキャッシュに格納されます。レスポンスヘッダもそのキャッシュに入るため、リクエストのたびにヘッダを作り直すことはありません。ディスク上のファイルが更新されればキャッシュのエントリも破棄されます。
Pragma: no-cache
などが付いたリクエストがあった場合には常にファイルの更新をチェックします。
戻る
Akira Higuchi