Linuxの話です。
続編(?)はこちら: [フラクタル] マンデルブロー集合 10の200乗倍への挑戦
先日、注目のコンパイラclang/LLVMがC++11に完全対応したというニュースがあったので、せっかくの機会なのでどんな具合なのか試してみました。
円周率ベンチはだいぶ下です。結論を言ってしまうと、速度はそこまで大きく変わりませんでした。<
まずはclang/LLVMのインストール。更新も早いし、基本的には本家ページのガイドラインに従うのがよいと思います。C++11に対応したのはつい最近なので、ソースコードをリポジトリから入手します。
% mkdir llvm
% svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
% cd llvm/tools
% svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
% cd ../projects
% svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt
コンパイルにはコンパイラやgmakeなどいくつか基本的なソフトが必要になります。本家ページにも書いてありますが、だいたいのLinuxディストリビューションにはあらかた対応しているかと思います。
ただしGCC4.3以前やARMでの4.6以前だとコンパイルにいろいろ問題があるようです。
気分と環境が整ったらコンパイルとインストールをします。
% ./configure --enable-optimized --enable-jit --enable-targets=x86_64 --prefix=${HOME}/どこどこ
% gmake -j コア数
% make check
% make install
オプションは適当に。–prefixオプションはなくても構いませんが、今回とは別にclangを既にインストールしている、root権限がないのでホームディレクトリ以下にインストールしたい、などの場合には指定しておきましょう。
その場合はインストールディレクトリにパスを通しておくことをおすすめします。
gmake -j の後は、CPUのコア数に応じて数字を指定しておくとそれなりに早くなります。
手元の環境では10分もあれば終わりました。
GCCのインストールがあれこれ依存してて泣きたくなるのに比べたら、LLVMのインストールは非常にシンプルです。この辺りはさすが後発。
test1.cppにHello Worldを書いて確認。しかもあえて間違えて。
#include <iostream>
int main(int argc, char** argv){
cout << "Hello, World!" << std::endl;
return 0;
}
そしてコンパイル。
% clang++ test1.cpp -o test1_clang
test1.cpp:4:2: error: use of undeclared identifier 'cout'; did you mean 'std::cout'?
cout << "Hello, World!" << std::endl;
^~~~
std::cout
/***/include/c++/4.7.2/iostream:62:18: note: 'std::cout' declared here
extern ostream cout; /// Linked to standard output
^
なかなか賑やかでフレンドリーなエラーが出力されました。(といってもこの程度ならg++でも比較的穏やかなエラーを返してくれますが。)
coutをstd::coutに書きなおして実行。無事Hello Worldです。
インストールだけで満足してもつまらないので、円周率の計算でベンチマークをとってみます。円周率の計算ソフトはいろいろありますが、今回は多倍長計算でGMP (The GNU Multiple Precision Arithmetic Library)を導入してGMP謹製の円周率計算ベンチを回してみます。
(なんでBSDライクなライセンスのclang/LLVMを準備したのにGNUプロジェクトのソフトを持ってくるのかという感もありますが、まあGMPはLGPLですし許してください。)
なお、遊びでなく本気のベンチマークが見たい方はPhoronixのページなどを参考にして下さい。
ベンチマークに円周率を使ったのは気分です。
GMPのサイトに行って最新バージョンを持ってきます。現時点では5.1.1が最新。GCCとclangでコンパイルします。
% mkdir gmp
% cd gmp
% wget http://ftp.jaist.ac.jp/pub/GNU/gmp/gmp-5.1.1.tar.bz2
% tar xvfj gmp-5.1.1.tar.bz2
% cd gmp-5.1.1
% ./configure --prefix=${HOME}/どこどこ/gcc-4.7 --enable-cxx CC=gcc CXX=g++
% make
% make check
% make install
% ./configure --prefix=${HOME}/どこどこ/clang-3.3 --enable-cxx CC=clang CXX=clang++
% make
% make check
% make install
GMPの円周率計算ベンチのページからソースコードを持ってきて、適当にpi.cなどの名前で保存します。
そしてコンパイル。-Iと-Lのパスは適当に読み替えてください。
% gcc pi.c -I../gmp/gcc-4.7/include -L../gmp/gcc-4.7/lib -lgmp -lm -O3 -o pi-gcc
% clang pi.c -I../gmp/clang-3.3/include -L../gmp/clang-3.3/lib -lgmp -lm -O3 -o pi-clang3.3
/tmp/pi-fDvkj6.o: In function `bs':
pi.c:(.text+0xc90): undefined reference to `fac_mul2'
pi.c:(.text+0xd89): undefined reference to `fac_mul2'
pi.c:(.text+0xdf8): undefined reference to `fac_mul_bp'
pi.c:(.text+0xeb7): undefined reference to `fac_mul_bp'
pi.c:(.text+0xed6): undefined reference to `fac_mul_bp'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
あれ?clangでリンカエラーが出るぞ?
ソースを見てみるとinline指定されている関数が見つからないことになっているもよう。プリプロセッサの問題かと思って-Eオプションつけてみても無事残っている。
…と調べてみたら本家に説明がありました。曰く、「C99の仕様ではinline指定をした関数定義はインライン展開されたときにのみ有効になるため、inline指定のない定義は別に必要になります。」とのこと。つまり、inlineと書いてあってもインライン展開されるかどうかはコンパイラが自由に決めることであり(clangでは「ゆるい提案」扱いになるとのこと)、インライン展開しないと決めたときに関数未定義になるらしい。
知らない。知らなすぎる。
clangで起きるエラーは実は手元のソースコードの仕様違反であった(がコンパイラの拡張やバグで潜在化していた)ということがよくあるようですが、今回もそうでした。コンパイラを変えてみるというのもそれだけで勉強になります。
まあ、今まで自分が知っていたと思っていた世界は実はC/C++でなくgcc/g++であった、というのが改めてよくわかります。
気を取り直して再コンパイル。inline 関数をstaticにするとかオプションに-std=gnu89をつけるとかいろいろありますが、単純にinlineを外すのが簡単です。どうせinlineつけてもclang最適化のほうが偉いので。
計測タイム。環境はXeon E5-2690 x2 (Sandy Bridge, 2.9/3.8GHz 2CPU/16コア/32スレッド)、メモリ16Gbyte、Red Hat Linux 64bit。 Xeonでコアが多いけど今回はマルチコア未対応なので関係なし。あえて言うならメモリ転送速度が4チャネルx2でやたら早い。
円周率100,000,000桁:
gcc : 111.126sec
clang : 111.309sec
あまり変わらない。桁をもっと増やしてみる。
円周率1,000,000,000桁:
gcc : 1664.053sec
clang : 1667.985sec
やっぱりほとんど変わらない。まあ、GMPが相当最適化されていてコンパイラの入る余地も少ないのでしょう。
個人的にはGMPをコンパイルできただけで私はだいぶ満足です。あと、円周率10億桁なんてとんでもないものが即座に計算できる時代になったことを再認識して喜んでいます。(clang関係ないけど)
それにしてもclangの進化はすごいですね。オープンソースでC/C++のコンパイラを作るなんて既にデファクトスタンダードのGCCもあるしで当分微妙なままなんじゃないかと思っていましたが、大間違いでした。セルフホスティングになってからあれよあれよという間にC++11完全対応まで来てしまいました。
clangはコンパイラとしてだけではなく、統合開発環境との連携も目玉のひとつです。今後Eclipseなど既存のIDEと深く関わっていけばコーディングがより気楽になるのでしょうか。
私も手元の遊びで書くC++はclangを使ってもいいかなと思っています。何よりエラーが見やすくて助かります。