gccとclangとicc
とある会話の中で、昔のプログラミング事情の話になった。
昔昔のその昔、世間のコンパイラが軒並み有料であった頃、コンパイラの価格によってプログラミング言語の選択が制限される時代があった。例えばC言語にしても、Windowsであれば「Borland C++ Compiler」や「LSI C-86試食版」、「Microsoft Visual C++」、「Intel C Compiler」等、各ベンダーがバラバラにコンパイラを制作していたためにベンダーの独自拡張の雨あられ、コンパイラ別に流派も別れた。
当時はC言語を勉強するにしてもそれらの中から「どのコンパイラを選ぶか」によって将来が左右された。私のようなアホでもない限り、いきなり30万円も出してMicrosoft Visual Studioを買う人間は居なかった。
さらに、コンパイラが高すぎるという理由で「Delphi」なんかを始めるプログラマや、Delphiこそ至高と考え「Kylix」に手を出して血涙を流したプログラマも居た。まさに当時はコンパイラ戦国時代。
そして現在はGCCの台頭から、各フリー(自由)コンパイラの充実、更にはそれらの普及からベンダー生コンパイラの消滅と、ベンダー生コンパイラのGCC互換と、時代の潮流は変化し、「コンパイラ販売」から、「開発環境販売」や「ライブラリ販売」にシフトしている。
きつねさんでもわかるLLVM ~コンパイラを自作するためのガイドブック~
- 作者: 柏木餅子,風薬
- 出版社/メーカー: インプレス
- 発売日: 2013/06/21
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (11件) を見る
そんな当時はコンパイラの性能からサポート機能、拡張機能、対応C言語のバージョン、実行速度やバイナリサイズなどピンキリであった。それ故に、C言語を学ぶものはコンパイラについても学ぶ必要があり、それぞれのコードに応じてコンパイラを使い分けたりもした。
だが現在C言語を学んでいる方々はこの時代の事を知らないようで(知らなくてもいいし)、コンパイラによる性能差がどれほどのものか理解されていないようだ。例えば当時ではアセンブリを読んで最適化について調べたりもしたようだが、現在はとにかく「-O2」をつければいいと考えられている。
まぁ、懐古主義でもないし知る必要のないことは知らなくてもいいと思うのだが、それぞれを「コンパイラ」としか認識していなければ特定のコードがボトルネックになっている際に、コンパイルオプションやコンパイラを変更するという選択肢が無いことになってしまう。
例えば現在多く利用されているC言語のコンパイラにはgccとclangがある。それに加えてベンダーの最高権力はCPUメーカが制作している「Intel C++ Compiler」だろう。最低でもこの3つについては簡単にでも知っておく必要はある。
例えば以下のプログラムがある。フィボナッチ数を計算する単純なコードだ。コンパイラの性能や最適化性能を測るにはもっとちゃんとしたコードであるが、こんな単純なコードでもコンパイラや最適化オプションでどれほど性能が変わるかという参考にはなる。
#include<stdio.h> int fib(int n){ if(n < 2) return n; return fib(n-2) + fib(n-1); } int main(void){ printf("%d\n", fib(48)); }
各コンパイラのバージョン
gcc | gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 |
clang | Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4) |
icc | icc (ICC) 14.0.3 20140422 |
gccとclangはUbuntu14.04のaptにあるバージョン。iccのみ最新版を利用。
実行時間はtimeコマンドで取得したのでバイナリサイズのオーバーヘッドもあるが、その分フィボナッチ数を大きく取ることで誤差を少なくしている。
実行環境のマシンは以下のスペック
PC | HP h8-1280jp |
CPU | Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz |
Mem | 16GB |
デフォルトでコンパイルした結果
コンパイラ | バイナリサイズ | 実行時間1 | 実行時間2 |
gcc | 8539 | 36.30s | 36.48s |
clang | 8619 | 36.45s | 36.75s |
icc | 20400 | 27.39s | 27.28s |
デフォルトでコンパイルした結果、iccが3割ほど早い。だが、以下の結果を見る限り、iccのデフォルトオプションが以下のものなのかもしれない。マニュアルを読まずにテストしたことを後悔している。
最適化オプションとして「-O2」をつけた結果
コンパイラ | バイナリサイズ | 実行時間1 | 実行時間2 |
gcc | 8545 | 19.23s | 19.10s |
clang | 8619 | 27.35s | 27.35s |
icc | 20400 | 27.22s | 27.12s |
clangとiccが同等速度となったが、gccがずば抜けて早い。昔はgccはマルチアーキテクチャ対応のために最適化性能が悪いと言われたものだがこれは早い。インテル製のコンパイラをよく利用していたが、今はもうgccが最適な気もした。
最適化オプションとして「-Ofast」をつけた結果
コンパイラ | バイナリサイズ | 実行時間1 | 実行時間2 |
gcc | 10029 | 15.16s | 15.07s |
clang | 8619 | 27.59s | 27.27s |
icc | 20400 | 27.30s | 27.20s |
このオプションはgcc以外で有効になるか確認していないが、clangでは「-O2」と同程度であるため有効にはなっているのかと思う。やはりgccが早い。もうインテル製は消そうかな。古いライセンスなんて役に立たないことも判明した。
アーキテクチャとして「-Ofast -march=native」をつけた結果
コンパイラ | バイナリサイズ | 実行時間1 | 実行時間2 |
gcc | 10029 | 15.66s | 15.61s |
clang | 8619 | 28.22s | 28.19s |
icc | 20400 | 28.34s | 28.20s |
このコードはアーキテクチャの命令セットでどうにかなるようなものではなかったので特に速度差は感じられなかった。念の為に「-march=corei7」を指定してみても同じであった。
このように、最適化オプションやコンパイラによって実行速度やバイナリサイズが大きく異なる。実行速度は最適化前と最適化後では倍以上の速度差が見られるし、コンパイラによってバイナリサイズが倍程度にもなる(バイナリサイズを削減するオプションは入れていない)。こんなシンプルなコードでもこのような差が出ることがわかっていただけたと思う。
なので現実的なコードであればもっと顕著な最適化が見られる上に、さらには自動並列化オプションなどを利用することでさらなる並列化の恩恵も受けられる。
それにしても、10年前の知識からインテル製が早いと勘違いしていたが、今はgccがかなり早いことを知れてよかった。普段使っている物もたまには性能試験をしてみるものだな。
ふつうのLinuxプログラミング 第2版 Linuxの仕組みから学べるgccプログラミングの王道
- 作者: 青木峰郎
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2017/09/22
- メディア: 単行本
- この商品を含むブログを見る
実例で学ぶGCCの本格的活用法―高機能コンパイラのオプション・コマンドを一つ一つていねいに解説 (TECHI―Embedded Software)
- 作者: 岸哲夫
- 出版社/メーカー: CQ出版
- 発売日: 2006/07
- メディア: 単行本
- 購入: 3人 クリック: 49回
- この商品を含むブログ (6件) を見る
GNU C COMPILER 増補改訂版 Manual&Reference
- 作者: 遠藤俊徳
- 出版社/メーカー: 秀和システム
- 発売日: 1999/04/12
- メディア: 単行本
- 購入: 1人 クリック: 23回
- この商品を含むブログを見る
- 作者: Robert Mecklenburg,矢吹道郎(監訳),菊池彰
- 出版社/メーカー: オライリージャパン
- 発売日: 2005/12/01
- メディア: 大型本
- 購入: 4人 クリック: 115回
- この商品を含むブログ (34件) を見る