チューニンガソンで優勝してきました
7/9(土)にチューニンガソン というイベントに参加して優勝してきたので、その報告と、何を考えてどんなチューニングをしたのかを 記憶の範囲で公開したいと思います。
今回のチューニンガソンのお題は、WordPress(ja) + php + Apache + MySQL で、 ab を使って wp-comment.php 経由でコメントのポストをすることで計測が行われました。 MySQLとApacheを立ち上げたらWordPressが動く環境が渡され、そのWordPress自体は設定ファイルを含めて 改造が一切禁止、WordPressの実行をショートカットするチートも禁止です。
0. 試合前日
環境がAWSとAMI Linuxということは事前に公開されていたため、前日にAWSに登録して少しだけAMI Linuxを 触ってみました。yumベースだけどCentOSと違って結構新しいバージョンが用意されており、 phpは5.3、Pythonは2.6.6がインストールされていました。 大抵のソフトはyumでインストールするだけで大丈夫そうです。
1. ボトルネックの確認
環境が配布される前に、チューニング対象とルールの説明が行われました。 WordPressは触ったことがないのですが、KLabが提供しているphp製のソーシャルアプリのチューニング経験から phpを実行するCPU時間がボトルネックになると予想していました。
この時点で、先日リリースされた php-5.4.0alpha のリリースノートにパフォーマンス改善と書かれていたことを 思い出し、php-5.4を試してみようと思い立ちました。
環境配布後、とりあえず ab をかけながら top を見てみて、予想通りhttpdのCPU使用率がボトルネックに なっていることを確認しました。
2. php-5.4のビルド
phpのビルドオプションを覚えていないので、yumでインストールされていたphpのphpinfoに 記載されている configure オプションをベースに php-5.4 をビルドしてみました。 どれくらい効果があるのかは解りませんが、一応 CFLAGS="-O3" も付けておきました。
yumでインストールされるものは付属の拡張モジュールを別パッケージでインストールできるように --without-mysql などでビルドされている事に気づかなかったり、少し手間取って、ここまでで 1時間以上経過していました。
並列して、phpには必須のアクセラレータも準備します。パッと思いつくのは eAccelerator と APC なのですが、KLabでの利用実績のある APC を第一候補にして、PECLのsvnリポジトリの履歴を チェックし、php-5.4で動きそうだと判断してチェックアウトとビルドをしました。
3. 総コメント数の罠に気づく
独自ビルドのAPC+phpが動くようになったあと、実際に投稿が実行できていることを確認するために、 MySQLに入ってテーブル構造を把握し、コメントテーブルに書き込みができていることを確認しました。
その後、いろいろ設定を試してみたものの、じりじりと性能が落ちていき若干パニックに なっていたのですが、コメントテーブルに大量に格納されたコメントを truncate したら性能が 復活しました。あとで聴いた話ですが、WordPressはコメントを書き込むときに何故か COUNT を実行しているらしいです。ボトルネックではない箇所でも動作を把握しておかないと、計測結果が 信頼できなくなるので要注意ですね。
この時点で、1000件のポストにかかる時間が48秒台でした。
4. perf を使ってさらにファインチューニング
perf を yum install perf でインストールし、abをかけながら perf top を観察してみました。
ここで、phpのメモリ管理の関数がほとんどのCPU時間を消費しているものの、プロファイル上位に Xenのハイパーバイザからメモリを取得しているものと思われる関数があることに気づきました。 仮想マシンだとコンテキストスイッチのオーバーヘッドが通常より増えますし、メモリをたくさん使うと ページフォルトが起こってハイパーバイザを経由するオーバーヘッドが増えると予想して、 メモリ使用量やプロセス・スレッドのチューニングを開始しました。
計測時のabオプションで並列度が20と規定されていたので、ApacheのMaxClientsを21、 MinSpareServersを20に設定しました。また、MySQLのmax_connectionsも20に設定した上で、 thread_poolも20にすることで、計測中に新規のプロセスやスレッドが一切立ち上がらない ようにしました。
さらに、チューニングを続けます。phpは内部でemallocという専用のメモリアロケータを持っていて、 これはセグメントと呼ぶ単位ごとにメモリをOSから取得し、そこからメモリブロックを切り分けています。 phpのshared nothingアーキテクチャに則り、このメモリアロケータにより確保されたメモリブロックは 全てhttpアクセス後に消されてしまいます。(つまりemallocを使っている限りHTTPリクエストをまたぐメモリリークは 存在しない)ただし、php-5.3から1セグメントだけHTTPリクエストをまたいで再利用するというチューニングが 行われており、このセグメントだけはページフォルトが少ない状態にできます。 このセグメントサイズは、 ZEND_MM_SEG_SIZEという環境変数で指定できるので、いくつかの設定を試してみて、 なんとなくよさそうな値として 128M を選びました。
ここまでで残り時間は30分を切っていて、簡単に実施できる効果がありそうなアイデアが無いし、 大きい変更をして逆効果だったり動かなくなった場合に元に戻せる自信がなかったので、一足先に ゴールして周りの人たちの作業を覗いたりしていました。最後に計測兼ウォームアップをし、この時の スコアは1000ポストで45秒を切っていました。
5. 計測と発表
最終的に、500ポストを22秒台で1位を取ることができました。 2位と3位は24秒台なので約1割の差を付けていたのですが、php-5.4を選んでいなかったら多分 負けていたと思います。php-5.4alphaリリースのニュースを最近見たのが本当に幸運でした。 上位陣が行った改良を全部組み合わせれば、20秒を切ることも可能だったかもしれません。
最後に
Webサーバーなんてスケールアウト簡単なんだからCPUネックになったら増やせばいいのですが、 少ない台数で多くのリクエストを捌くことにも意味はありますし、何より楽しいですよね、チューニングって。
こんな楽しいイベントを開催してくださった運営の皆様やスポンサーの皆様、本当にありがとうございました。