奥さんの言う通りである。指摘をもらってから気がついた。反省した。それからからずっと「まっとうにコンパイルして30秒を切る方法」を模索してきた。そしてついに、ccacheを使わずにまっとうにMySQL 5.5のコンパイルを30秒未満で実行することが出来たので、その方法を紹介しようと思う。
速いマシンを買う
いきなり身も蓋もない解決法だが、ぶっちゃけこれが一番効果的である。実行するべき処理が決まっていれば、最終的にCPUの実行速度によって処理時間が決まってしまう。
実は最近PCを新調したのだが、そのPCでコンパイルするとあっさりと目標を突破してしまった。剛よく柔を制す。力こそパワー!・・・だが虚しかった。
「CPUの性能の違いが、戦力の決定的差ではないということを教えてやる!」
そう言おうと頑張って来たのだが、ついにその台詞を言える日は来なかった。俺は負けたのだ。だから改めて言おう。
「CPUの性能の違いは、決定的な戦力差です。」
とは言え、マシンの性能向上によって様々な問題が解決してしまうのはIT業界の常である。今回はそのことについて身を持って知ることになった。これからも恐らく思い知らされるのだろう。だが、いつの日か私も言ってみたい。シャアと同じ台詞を。
ところで、新しいPCについては、また改めてエントリで紹介しようと思う。
可能な限りソフトウェアをチューニングする
ところで、最近新調したマシンのCPUはi7-3740QMである。このCPUでは、何も工夫なしで30秒を切ることはできなかった。そこで改めて言おう。
「CPUの性能の違いだけが、戦力の決定的差ではない」と。最終的に目標を達成するには、少しだけ工夫が必要である。その工夫の一つ目が、ソフトウェアのチューニングである。
具体的にはOSカーネル、libc、gcc、binutils等のソフトウェアが最高のパフォーマンスを発揮できるよう自らコンパイルするのである。これは前回のエントリを書いたときにも有効な方法であった。
だったら最初から全部コンパイルするOSを使えばいいじゃない。
そういう結論に至るのはごくごく自然なことであると言えよう。すなわち、新しいマシンにインストールすべきOSはGentoo Linuxということである。言うまでもないが、Gentooには自分で各種ソフトウェアをコンパイルするための環境はばっちり揃っている。というかコンパイルしなければ先には進めないと言うべきか。だが敢えて言おう。Gentooは夢を叶えてくれるディストリビューションである
最適化をしない
マシンパワーの次に重要な点は、MySQLをコンパイルするときに最適化オプションをオフにするということだ。具体的には-O0フラグを指定する。
ソフトウェアのコンパイルにおいて何が一番時間がかかるのか。それは最適化である。文法の解析などは最適化に比べればはるかに軽い処理であると言える。-O2と-O0では、ほぼ倍ぐらい時間に差が出てしまう。とはいえ、当然最適化オプション(-O2など)をつけてコンパイルされたバイナリのほうが実行速度は速い。従って、現実には、最適化オプションなしでのコンパイルは、あくまでも実行速度よりもコンパル速度のほうが重要だというニーズがある場合(例えばMySQLを改造して繰り返しデバッグする場合)などに役立つテクニックだと言える。そういうニーズがある場合には劇的にコンパイル速度が変わるので是非試して欲しい。
shell> export CFLAGS='-O0 -m64' shell> export CXXFLAGS="$CFLAGS"
ちなみに、-O0により以前のマシンでは60秒を切ることはできた。(しかし、50秒の壁は突破できなかった。)
古いバージョンのgccを使う
これも先ほどの話に通じる部分があるのだが、新しいバージョンほど新しい最適化アルゴリズムが追加されており、コンパイルに時間がかかるようになっている可能性がある。ただし、バージョンが上がることでコンパイラ自身の最適化が行われることもあるので、一概に古い方が良いとは言えないのではないかという予想もある。また、64ビット版と32ビット版でも異なる性能特性になるのではないか。ということで最新バージョンのgccだけでなく、実際にいくつかのバージョンでコンパイルにかかる時間を計測してみた。
gcc-3.4 64bit real 0m 41.03s user 4m 42.76s sys 0m 19.04s gcc-3.4 32bit real 0m 40.30s user 4m 38.49s sys 0m 18.48s gcc-4.1 64bit real 0m 26.90s user 2m 59.49s sys 0m 13.75s gcc-4.1 32bit real 0m 26.41s user 2m 54.28s sys 0m 13.39s gcc-4.3 64bit real 0m 29.37s user 3m 14.23s sys 0m 15.23s gcc-4.3 32bit real 0m 28.25s user 3m 9.59s sys 0m 14.92s gcc-4.6 64bit real 0m 32.46s user 3m 40.90s sys 0m 14.95s gcc-4.6 32bit real 0m 32.20s user 3m 37.38s sys 0m 14.62s gcc-4.7 64bit real 0m 32.57s user 3m 41.75s sys 0m 14.74s gcc-4.7 32bit real 0m 32.00s user 3m 37.78s sys 0m 13.44s
これをグラフ化したものが次の図だ!!
最高記録はなんと26.41秒。(GCC 4.1.2 / 32ビット)大幅に30秒を切ることに成功した。なお、いずれのバージョンでも、64ビット版よりも32ビット版のバイナリをコンパイルするほうが高速であった。
計測時はCPUは当然ながらフル回転していた。topの出力はこんな感じ。us + sy = 100なので完璧に使い切っている。無駄がなくて素晴らしい。
top - 00:30:17 up 3:27, 5 users, load average: 9.47, 5.85, 3.09 Tasks: 239 total, 9 running, 230 sleeping, 0 stopped, 0 zombie %Cpu(s): 93.9 us, 6.1 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem: 32600520 total, 4431904 used, 28168616 free, 777940 buffers KiB Swap: 67108860 total, 0 used, 67108860 free, 2031960 cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 9362 mikiya 20 0 230124 107148 4088 R 28.58 0.329 0:00.86 cc1plus 9374 mikiya 20 0 212540 87752 2824 R 21.60 0.269 0:00.65 cc1plus 9380 mikiya 20 0 208188 84284 2824 R 20.94 0.259 0:00.63 cc1plus 9386 mikiya 20 0 194284 70136 2824 R 15.95 0.215 0:00.48 cc1plus 9392 mikiya 20 0 190048 65484 2824 R 14.62 0.201 0:00.44 cc1plus 9399 mikiya 20 0 159220 34692 2788 R 4.653 0.106 0:00.14 cc1plus 9410 mikiya 20 0 154160 29372 2652 R 2.659 0.090 0:00.08 cc1plus 9416 mikiya 20 0 152644 27516 2160 R 1.994 0.084 0:00.06 cc1plus
その他のコンパイラオプション
-O0はコンパイル時間を劇的に改善するが、他にも次の2つのオプションをつけることで若干の性能改善が見られた。
- -pipe。これはプロセス間の情報の受け渡しに、ファイルの代わりにパイプを使うようにするというものだ。これによりオーバーヘッドが少なくなってコンパイル時間が気持ち早くなる。
- -w。これは全ての警告を表示しないようにするもの。今回は時間の測定なので警告はどうでも良いので非表示にした。
また、最近「本の虫」で紹介されていたGentooを最速でブートせよ というエントリで、busyboxにすることで起動が早くなったというのを見たのでbusyboxを試してみたところ、若干の性能改善が見られた。
実際の計測は次のようなスクリプトで行い、3回のうちベストのものを結果として採用した。結果さえ分かれば良いので、余計な出力はすべて/dev/nullに叩きこんである。(そうすることで少しでも余計なことにCPUが奪われるのを避けるためでもある。)
for arch in 32 64 do export CFLAGS="-m${arch} -g0 -O0 -pipe -w" export CXXFLAGS="$CFLAGS" rm CMakeCache.txt cmake ..>/dev/null make clean time make -j 9 >/dev/null make clean time make -j 9 >/dev/null make clean time make -j 9 >/dev/null done
禁断の果実、clang
さて、最近はどうもclangが勢いを増してきているようである。そして、clangはコンパイルが速いというもっぱらの評判だ。GNU支持者としてはGPLを排除しようという動きは非常に心苦しい限りだが、比較しないのはフェアではないので試してみることにした。その結果、確かに速かった。
clang 3.3 64bit real 0m25.222s user 2m46.394s sys 0m10.417s clang 3.3 32bit real 0m25.701s user 2m50.028s sys 0m10.705s
なんと、25秒台が出てしまった。gccのベストに1秒以上の差をつけての勝利である。実際の利用シーンではほとんど誤差のようなものであるとは考えられるが、clangは確かに速かった。しかし、clangではbusyboxを使った場合に64ビット版コンパイル時にエラーが出るといった問題もあった(原因は調べていない)ので、やはり安定性という点ではまだ不安があるように思う。
バイナリの性能
最後に、コンパイラによる最適化の違いでどの程度性能に差が出るのかを示しておこうと思う。結論から言うと、やはり-O0で生成したバイナリはコンパイラに関わらずかなり遅いと言える。-O0はテスト時など、とにかく速くコンパイルしたい場合だけ使用するべきだろう。gccとclangの差はほとんど誤差に近い。
まとめ
今回、ようやく字面どおり30秒以下でMySQL 5.5をコンパイルすることが達成できた。やはり-O0の効果は絶大であり、そして速いCPUは正義である。コンパイル時間のチャレンジは面白いので、今後も引き続き行おうと思う。また進展があれば(何年後になるかはわからないが)報告したいと思う。
0 コメント:
コメントを投稿