レベル: 中級 Martin Streicher (mstreicher@linux-mag.com), Editor in Chief, Linux Magazine
2007年 2月 20日 PHP は、Web アプリケーションの作成に最も頻繁に使用されるスクリプト言語です。PHP は学びやすく、目に見える結果を即座に作成します。しかし PHP はインタープリター型のため、PHP コードは実行の都度、構文解析とオペ・コードへの変換が必要です。オペ・コード・キャッシュによって、そうしたリワークがなくなり、PHP アプリケーションが高速化されます。
PHP は、Web アプリケーション用のプログラミング言語として、あっという間に一般的になりました。初心者にとって、PHP は簡単にインストールでき、容易に学ぶことができます。また経験の長い開発者にとっては、PHP は (V5 の時点で) 強固なオブジェクト指向機能を提供しています。PHP 開発者のコミュニティーは巨大であり、また多数のオープンソースや商用のライブラリーやツールによって、コア言語の機能を拡張することができます。PHP がこれほど多くの人達に好まれているのは、目に見える結果を即座に得られるからです。
PHP コードは、Web アプリケーションに使用されている他のスクリプト言語 (Perl や Python、Ruby など) と同様、HTTP リクエストに呼び出される度に、必ず毎回、構文解析され、オペ・コード (PHP エンジンが直接実行する (アセンブリー言語に似た) 基本命令) に変換されます。ごくわずかの少ない要求の場合には、サーバーはこの複雑な描画処理を、一見すると瞬間的に実行します。しかし提供するページが増えると、解釈 (つまりリワーク) によるシステムへの負荷がかかってきます。場合によると、PHP コードを「コンパイルする」時間の方が、コードの実行に必要な時間を大幅に上回ってしまいます。従って、要求が増大すると、皆さんはすぐに成功の犠牲者となるのです。これは、動的に解釈され、動的に生成されるページの提供数が増大することによって、より一層多くのリソースが要求されるようになるためです。
もし皆さんのサイトがプロセッサーと RAM のために無限の予算を持っているのであれば、アプリケーション・スタック (ハードウェア、オペレーティング・システム、データベース、Web サーバー、そして PHP コード) を最適化してサイトの応答を維持する必要はないかもしれません。しかし、予算というのは一般的には最も不足するリソースであるため、パフォーマンスの調整は避けられません。この調整という意味は、不足しているメモリーを追加することの場合もあれば、オペレーティング・システム・パラメーターの変更、Web サーバーまたはデータベース・サーバーの高速化、コードの書き換えによる効率化、あるいはそれらの組み合わせなどの場合があります。どれも、高速化に対して効果があります。
捨てずにリサイクルしましょう
CPU サイクルを節約するための、もう 1 つの方法は、PHP アプリケーションを実行するために必要なリワークの量を単純に減らすことです。たしかに、同じ PHP コードを毎回変換する必要はないのです。いったん PHP コードがオペ・コードに変換された後は、オリジナルのコードが変更されるまで、そのオペ・コードは保存でき、何度も何度も再利用できるのです。実際、キャッシング、つまり中間 PHP コード (オペ・コード) を保存して再利用することが、いくつかの PHP アクセラレーターの背景概念となっています。こうしたアクセラレーターとしては、オープンソースの APC (Alternative PHP Cache) や Turck MMCache for PHP、XCache、eAccelerator、そして商用の Zend Platform などがあります。これらのうちの最後の 3 つは、バイトコードをキャッシュして最適化するため、より一層の高速化を実現します。
今月は、XCache をインストールし、デプロイし、構成する方法を探ります。XCache は比較的新しいものですが、多くのサイトが XCache を使って好結果を得られたと報告されています。しかも、XCache は PHP のエクステンションとして実装されるので、ビルドやインストール、構成が容易です。Apache と PHP を再コンパイルする必要はありません。
この記事は XCache V1.2.0 に基づいています。XCache は、PHP V4.3.11 から V4.4.4 まで、PHP V5.1.x から V5.2.x まで、そして初期バージョンの PHP V6 を安定してサポートしています。 (XCache は PHP V5.0.x をサポートしていません。) XCache は mod_php と FastCGI では動作しますが、CGI (Common Gateway Interface) やコマンドライン PHP インタープリターでは動作しません。XCache のソース・コードは、FreeBSD や Sun Solaris、Linux®、そして (ここで示すように) Mac OS X など、さまざまなシステムでビルドすることができます。また XCache は、Cygwin UNIX® エミュレーション環境や Visual C を使うことで、Microsoft® Windows® でもビルドすることもできます。XCache を、Cygwin 用に、あるいはネイティブの Win32 用にビルドすることもできます。後者のターゲットは、PHP の正式な Win32 リリースと互換性があります。
ここで示すデモは、Apache V2.2.3 と PHP V5.2.0、XCache V1.2.0 (2006年12月10日リリース)、そして Mac OS X V10.4.8 Tiger での Xcode V2.4.1 に基づいています。ハードウェア・プラットフォームは、2-GHz の Intel® Core Duo と 2 GB の RAM を搭載した Apple MacBook です。
XCache を構築するための順を追った説明
この先を続ける前に、インストールされた PHP がそのままで動作すること、phpize がシェルの PATH にあることを確認します。また、GCC (GNU Compiler Collection) などの C コンパイラーと、make と m4 を含む開発ツール一式も必要です。Mac OS X の場合は、必要なビルド・ツールをフリーの Xcode ソフトウェア開発環境が提供しています。
下記の手順に従って Mac OS X 上で XCache をビルドし、デプロイし、ベンチマークを行います。他のプラットフォームでのビルドも同様です。Linux を使っている場合には、そのディストリビューションに既に XCache が含まれているか、あるいはパッケージ化された形で提供されているかもしれません。
Mac OS X の共有メモリーを増加する
まず、Mac OS X で確保している共有メモリーの量を増やすことから始めます。そのためには、ファイル /etc/sysctl.conf を作成 (あるいは編集) し、次のようなエントリーを作成します。
リスト 1. Mac OS X で確保している共有メモリーの量を増やす
kern.sysv.shmmax=33554432
kern.sysv.shmmin=1
kern.sysv.shmmni=32
kern.sysv.shmseg=8
kern.sysv.shmall=8192
|
こうした設定によって、共有メモリーの量が 32 MB に増加します。さらに共有メモリーを拡張する必要がある場合には、kern.sysv.shmall を、kern.sysv.shmmax をハードウェア・ページ・サイズで割った値に設定します (ハードウェア・ページ・サイズは sysctl hw.pagesize を使って調べることができます)。例えば128 MB の共有メモリーが必要な場合には、kern.sysv.shmmax=134217728 と設定し、また kern.sysv.shmall=32768 と設定します。
この変更を実現するために、Mac OS X を再起動します。再起動したら、下記を入力し、新しい設定が有効になったかどうかを調べます。
sysctl -a | grep kern.sysv
|
ソースから XCache をビルドする
次に、ソース・コードから XCache をビルドします。ソースを http://xcache.lighttpd.net からダウンロードします。コードを取得できたら、解凍し、.tar ファイルが作成する新しいディレクトリーに移ります。
リスト 2. ソースから XCache をビルドする
$ cd /tmp
$ wget http://210.51.190.228/pub/XCache/Releases/xcache-1.2.0.tar.gz
$ tar xzf xcache-1.2.0.tar.gz
$ cd xcache
|
phpize を実行し、XCache をコンパイルする準備をします。
リスト 3. phpize を実行する
$ phpize
Configuring for:
PHP Api Version: 20020918
Zend Module Api No: 20020429
Zend Extension Api No: 20050606
|
configure を実行し、ネイティブ・オペレーティング・システムにあった makefile を作成します。
リスト 4. configure を実行して makefile を作成する
$ ./configure --enable-xcache --enable-xcache-coverager
checking build system type... i686-apple-darwin8.8.1
checking host system type... i686-apple-darwin8.8.1
...
creating libtool
configure: creating ./config.status
config.status: creating config.h
|
ここで、--enable-xcache オプションによって XCache サポートが含まれ、また --enable-xcache-coverager によって、アクセラレーターの効果を測定するための追加機能が含まれます。オペ・コードのオプティマイザーを有効にするためには、--enable-xcache-optimizer を追加します。
もちろん、次のステップでは make コマンドを使ってコードをビルドし、インストールします。make を実行し、次に、ルートとして make install を実行します。
リスト 5. make を使ってコードをビルドし、インストールする
$ make
...
cp ./xcache.so /Users/strike/tmp/xcache/modules/xcache.so
Build complete.
$ sudo make install
Installing shared extensions: /usr/lib/php/extensions/no-debug-non-zts-20020429/
|
この 2 つのアクションがスムースに進めば、XCache が /usr/lib/php/extensions/no-debug-non-zts-20020429/xcache.so にあるはずです。(パス /usr/lib/php/extensions/no-debug-non-zts-20020429 は、使用されている API のバージョンと、PHP のビルドに使用されたコンパイル・オプションを反映しています。もし実験的な Zend Thread Safety 機能を有効にした場合には、「no-debug」は「debug」になり、「non-zts」は「zts」になります。)
php.ini を変更する
エクステンションがインストールできたので、XCache エクステンションを組み込んで構成できるように、php.ini ファイルを変更する必要があります。/private/etc/php.ini を開き、このファイルに下記の行を追加します。
リスト 6. XCache エクステンション用に php.ini を変更する
[xcache-common]
zend_extension = /usr/lib/php/extensions/no-debug-non-zts-20020429/xcache.so
[xcache.admin]
; Change xcache.admin.user to your preferred login name
xcache.admin.user = "admin"
; Change xcache.admin.pass to the MD5 fingerprint of your password
; Use md5 -s "your_secret_password" to find the fingerprint
xcache.admin.pass = "0ad72f3f352fcd8acdf266bafd0ac48d"
[xcache]
; Change xcache.size to tune the size of the opcode cache
xcache.size = 24M
xcache.shm_scheme = "mmap"
xcache.count = 2
xcache.slots = 8K
xcache.ttl = 0
xcache.gc_interval = 0
; Change xcache.var_size to adjust the size of variable cache
xcache.var_size = 8M
xcache.var_count = 1
xcache.var_slots = 8K
xcache.var_ttl = 0
xcache.var_maxttl = 0
xcache.var_gc_interval = 300
xcache.test = Off
xcache.readonly_protection = On
xcache.mmap_path = "/tmp/xcache"
xcache.coredump_directory = ""
xcache.cacher = On
xcache.stat = On
xcache.optimizer = Off
[xcache.coverager]
xcache.coverager = On
xcache.coveragedump_directory = ""
|
注意: 一部のコメントは、簡単にするために省略されています。各パラメーターが何をコントロールするのかを知るには、XCache のソース・コードに含まれている xcache.ini ファイルの中のサンプル設定を見てください。
オペ・コードと可変キャッシュのサイズは 32 MB であり、これは /etc/rc に確保してある最大値です。Mac OS X の場合、ファイル名は xcache.mmap_path である必要があります。またこの PHP コードは MacBook 上で実行するので、xcache.count は MacBook の CPU の個数である 2 に設定されています。XCache の統計ページにアクセスするには、xcache.admin.pass 設定を変更する必要があります。そこで下記を実行します。
ここで password は皆さんのパスワードです。この出力を xcache.admin.pass にコピーします。例えば、パスワードを op3nsesam3 にしたい場合には、下記を実行します。
$ md5 -s "op3nsesam3"
MD5 ("op3nsesam3") = cd959ac3debe8f587546a3fa353b3268
|
そして cd959ac3debe8f587546a3fa353b3268 を xcache.admin.pass にコピーします。
Web サーバーを再起動する
XCache の設定ができたら、Apache Web サーバーを再起動します。大部分のシステムでは、ルートとして apachectl restart を使うことができます。
$ sudo apachectl restart
/usr/sbin/apachectl restart: httpd restarted
|
phpinfo() をコールするプログラムを作成する
XCache が有効になっていることを確認するために、phpinfo() をコールする小さな PHP プログラムを作成し、このファイルを Web ブラウザーで開きます。そうすると下記のような XCache セクションが表示されるはずです。
図 1. phpinfo() メソッドで XCache の設定を要約する
XCache をモニターする
XCache をモニターするには、XCache のソース・コードの admin ディレクトリーにある管理ページをインストールします。admin ディレクトリー全体を、Apache のドキュメント・ルートにコピーします。Mac OS X の場合、ドキュメント・ルートは通常 /Library/WebServer/Documents です。
$ cp -pr admin /Library/WebServer/Documents
|
コピーが完了したら、sudo apachectl restart を使って Web サーバーを再起動します。管理パネルが動作することを確認するために、ブラウザーで http://localhost/admin を表示します。そうすると図 2 のようなダッシュボードが表示されるはずです。
図 2. XCache の管理ダッシュボード
アプリケーションをテストする
テストするアプリケーションを 1 つか 2 つ選択します。皆さん自身のコードを使うこともできますが、もっと複雑なものが必要であれば、phpMyAdmin や Serendipity のような大きな PHP アプリケーションを試してみてください。
ベンチマークを実行する
Apache HTTP Web サーバーには、ab という名前のユーティリティーが同梱されています。これは Apache HTTP server benchmarking tool を略したものです。ab を使うと、PHP ページへの大量のリクエストを自動化することができます。phpMyAdmin アプリケーションは、おそらく既に皆さんのシステムにインストールされているので、候補として適切です。
ab ユーティリティーの使い方は簡単であり、繰り返し回数と URL を与えるだけです。ab ユーティリティーは、その URL を何度も繰り返しリクエストし、統計結果を返します。XCache は既に有効になっているので、最初のベンチマークはアクセラレーションが有効な場合のパフォーマンスを示します。
ab を実行する前に、ブラウザーで http://localhost/phpmyadmin/ を表示します。このページを 1 度訪れると、このページの描画に使われたすべての PHP コードがキャッシュにロードされます。これで下記のベンチマークを 100,000 回繰り返して実行します。
リスト 7. phpMyAdmin に対するベンチマーク
$ ab -n 100000 http://localhost/phpmyadmin
...
Concurrency Level: 1
Time taken for tests: 14.597 seconds
Complete requests: 100000
Failed requests: 98262
(Connect: 49131, Length: 49131, Exceptions: 0)
Broken pipe errors: 0
Non-2xx responses: 50869
Total transferred: 25739714 bytes
HTML transferred: 12005084 bytes
Requests per second: 6850.72 [#/sec] (mean)
Time per request: 0.15 [ms] (mean)
Time per request: 0.15 [ms] (mean, across all concurrent requests)
Transfer rate: 1763.36 [Kbytes/sec] received
|
関心の対象となる統計は、1 秒当たりのリクエスト数と、すべてのテストを完了するまでの合計時間です。前者の場合、値が高いほど優れており、後者の場合は値が低いほど優れています。
今度は php.ini ファイルの XCache を無効にし、ベンチマークを再度実行します (リスト 8)。そのためには、XCache エクステンションへの参照をコメントアウトするか、あるいは XCache の全機能をオフします。再度ベンチマークを実行する前に、Aapche を再起動します。
リスト 8. XCache を無効にした状態での phpMyAdmin に対するベンチマーク
$ sudo apachectl restart
$ ab -n 100000 http://localhost/phpmyadmin
Concurrency Level: 1
Time taken for tests: 17.771 seconds
Complete requests: 100000
Failed requests: 98256
(Connect: 49128, Length: 49128, Exceptions: 0)
Broken pipe errors: 0
Non-2xx responses: 50872
Total transferred: 25741232 bytes
HTML transferred: 12005792 bytes
Requests per second: 5627.15 [#/sec] (mean)
Time per request: 0.18 [ms] (mean)
Time per request: 0.18 [ms] (mean, across all concurrent requests)
Transfer rate: 1448.50 [Kbytes/sec] received
|
この場合は XCache が無効になっているため、1 秒当たりのリクエスト数は低下しており、Apache サーバーで各リクエストの処理時間が長くなっていることが反映されています。また、テスト全体を実行するための時間も増加しています。
これは単純なベンチマークであり (データベースへの phpMyAdmin 接続を無効にし、処理時間を PHP の解釈時間のみに制限することもできたのですが、そうはしていません)、また高度に科学的でもありませんが、XCache で何が可能かをよく示しています。ごくわずかな投資で (そして、幸い PHP や Aapche を再コンパイルする必要はないため)、XCache がもたらす利益は比較的大きなものです。コードが複雑であればあるほど、得られる利益も大きい可能性があるのです。
XCache の動作に関心がある方は、http://localhost/xadmin を訪れ、List PHP をクリックしてみてください。キャッシュの中にある PHP ファイルのリストの他、キャッシュ・ヒットや、オペ・コード単位で測定されたコード・サイズ、バイト単位で測定されたソースファイル・サイズなど、さまざまなものを見ることができます。図 3 は、特に XAMPP スタック・パッケージに対して XCache がビルドされた場合の結果の一部を示しています。
図 3. XCache 管理ページがキャッシュの状態と内容を反映している
先ほど触れたように、XCache はいくつかあるアクセラレーター・オプションの 1 つです。他にもフリーでオープンソースのアクセラレーターもあり、また商用オプションとして、強力な Zend ソフトウェアもあります。それぞれの PHP アクセラレーターには独自のシステム要求があるため、どれを選択するかは、皆さんの既存の構成、あるいは求める構成と、アプリケーションの特性に大きく依存します。どれを推薦するかは難しいですが、コンパイラー・キャッシュのインストールはお薦めします。
無限にある調整オプション
アプリケーションを高速化するためには、キャッシングの他にも数多くの方法を探ることができます。PHP エンジンから余分なものを取り除くのもその方法で、より難解な機能の多くを削除することができます。例えば、TCP/IP V6 (IPv6) ネットワークを使わないのであれば、PHP をビルドする際にその機能を無効にします。PHP の構成オプションの全リストは、PHP ソース・コード・ツリーの先頭で ./configure --help を入力すれば見ることができます。どの構成オプションを選択する場合にも、オプションの最後に下記を追加します。
--enable-inline-optimization --disable-debug
|
前者のオプションは (Zend Engineのようなソフトウェアによるオペ・コードの最適化を追加することなく)、可能な範囲で最も高速な PHP 実行可能ファイルを作成します。また後者のオプションは PHP のデバッグ・モードを停止します (デバッグ・モードは PHP アプリケーション・サーバー自体の問題を解決しようとする場合以外は必要ありません)。
もちろん、C のアプリケーションとまったく同じように、C コンパイラーを活用して、より良い実行可能ファイルをビルドすることもできます。PHP を x86 プロセッサーの Linux 上あるいは FreeBSD で Apache DSO (Dynamic Shared Object) として実行する場合には、CFLAGS (C コンパイラー・オプションを保存する環境変数) に -prefer-non-pic オプションを追加することを検討します。non-PIC によって、場所に依存しないコードを使う PHP がビルドされ、10% 程度の高速化が図れます。また、CFLAGS で -march を使うと、プロセッサー・タイプを特定することができます (例えば AMD の Opteron プロセッサーに対しては -march=opteron など)。
高速化のための、もう 1 つの方法がオペ・コードの最適化です。この場合は、Zend Engineのようなソフトウェアが、コンパイル中に作成されるオペ・コードの最適化を行い、文字通りコードの実行作業量を削減します。
キャッシングと最適化はわかりやすく、特別なプログラミング能力は必要ありません。少し力仕事をしようとするのであれば、プロファイリングの追求、つまりコードがどこで時間を消費しているかの検証を行います。ボトルネックは当然ながらリワークの対象ですが、無駄なアルゴリズムや遅いアルゴリズムも同じく対象とすべきです。コードを細工して CPU サイクルを絞り出すのは貴重なことですが、最初にプロファイリングを行わずに最適化しようとすべきではありません。
この先は
最適化に関しては、今後再度取り上げる予定です。また、デバッグや高速テキスト検索、代替 Web サーバーなどに関しても取り上げます。それまでの間、1 つあるいは複数の PHP アクセラレーターとオペ・コード・オプティマイザーを試してみてください。数時間いじってみるだけで、10% から 200% の速度増加を容易に実現できるのです。そうして得られる余分なサイクルで、皆さんのマシンがどれほど多くのことをできるか想像してみてください。
参考文献 学ぶために
製品や技術を入手するために
議論するために
著者について  | |  | Martin StreicherはLinux Magazineの編集長です。以前はBerkeley Systemsのエグゼクティブ・プロデューサーとして、YOU DON'T KNOW JACK(ゲーム)やAfter Darkを含め、様々な賞を受けたソフトウェアを開発してきました。彼はPurdue Universityにて、コンピューター・サイエンスで修士を取得しています。卒業後初めての仕事は、CONVEXスーパーコンピューター用のUNIXプログラミングでした。 |
記事の評価
|