2010-3-12[金] ちょっと速度比較

前回触った LLVM-gccやpcc,coins (で作ったプログラム)の速度が気になったので、 手持ちのCコンパイラ(vc意外はフリーのもの)で適当に実行速度を計ってみた。

(3/14修正: cygwinの計測が間違って別のmingwでの結果になってました).
(3/15修正: llvm-gccは生成方法を間違えていたためやり直しました. また他と比較しずらいので clangの結果も含めました)

試したコンパイラは全てwin環境用で

コンパイラ 生成にかかわるオプション補足
vc9sp1 64bit -GR- -Ox -DNDEBUG -MT
vc9sp1 32bit -GR- -Ox -DNDEBUG -MT
cygwin gcc 3.4.4 -O2 -DNDEBUG bashでなくcmd.exe上で実行.
mingw32 gcc 3.4.5-O2 -DNDEBUG
mingw32 gcc 4.4.0-O2 -DNDEBUG
LLVM-gcc 2.6 -O2 -DNDEBUG -emit-llvm 実際には一度に作れず複数工程になる
clang -O2 -DNDEBUG -emit-llvmせずで、おそらくclang単体での生成
Open Watcom 1.8 -Ot -O3 -DNDEBUG -finline-functions
-frerun-optimizer -floop-optimize
-funroll-loops -finline-math
dmc 8.51 -o -DNDEBUG
pcc-20090818-win32-O3 -DNDEBUG
COINS 1.4.4.3-en -O2 -DNDEBUG bashでなくcmd.exe上で実行
BorlandC 5.5.1 -O2 -Ox -RT- -d -dc -DNDEBUG
PellesC 6.00.6 /Zx /Ze /Ox -DNDEBUG
TinyCC 0.9.25 -DNDEBUG オプティマイズは無い

正直、オプションが、どの適度適切かは不明。 Watcomはつけすぎてるけど、基本はデフォルトに近い状態。
浮動小数点関係は、設定しだいで結構違うだろうけど不精してせず(watcom以外)。

試したテストプログラム

バイリニアによる画像拡大 乱数で生成したピクセルで埋め尽くされた640x480の32ビット色(ARGB)画像を1920x1080へ拡大する処理。10回の平均値。
安易インタプリタでのスクリプト実行 400行ほどのおもちゃのインタプリタ言語で、 5000*5000ループの適当なサンプル実行. (仕組みが手抜すぎて実際的な比較向けではなかったかも)
クィックソート 以前試したCでかかれたQUICKソートと、Cライブラリのqsort互換のクィックソートルーチン のテスト.
とりあえず、この頁の結果には int 1000000個をソートしたときの時間を記入.
Dhrystone 2.1 整数演算用ベンチマーク. Wikipedia等みて. モノは、こちらの記事から リンクされているモノを使用. ソースは2.1のモノだけ使用(コンパイル楽なため). けど、Old-Cスタイルでエラーだすコンパイラ対策でANSI-Cスタイルに修正したり ヘッダ依存関係調整したり. 使用時はループ回数を100000000回にして実行.
Whetstone 浮動少数点演算用ベンチマーク. wikipedia等みて. こちらのサイトの BenchNT.zip に入っていたソースを利用. こちらもコンパイラごとの調整のための修正あり.

download:上記のソースのzip

たくさんやるのも面倒なので、これだけ(これだけでも結構しんどく)。 自分が大雑把に雰囲気をつかむため、なので、あまり適切でないサンプルかも。 1core 1threadな簡易な処理だけだし。 Dhrystone,Whetstoneは一応してみたけど、あまりピンときていない (処理内容わかってないからだろうけど).


実行環境は vista64(PhenomIIx4 3G)、通常のデスクトップでDOS窓とエディタ程度だが、サービスや常駐モノはある程度残った状態で実行.(テスト中の 1core がフル稼働で、あとの処理は他のcoreが大筋まかなってるだろうと期待. 以前別の計測にてsafemodeで起動して計測したほうが安定していた感じはあったけれど、酷すぎるほどの差もなかったので楽してる)

実行結果 は (3/14 一部計測し直し. cygwin計測ミス、安易インタプリタのclang対処反映)

コンパイラBilinear 拡大(ミリ秒)安易 インタプリタ(秒)quick sort(μ秒)QSORT(μ秒)Dhrystone/secWhetstone (MWIPS)
vc9sp1 64 87.525.21443939856142146413465.753
vc9sp1 32 189.224.71445269039115300362284.091
cygwin 344262.020.22030060900102082482108.046
mingw 345 245.025.4191666155698931541456.068
mingw 440 265.025.31911885483102732691554.432
LLVM-gcc(sse2)105.827.61987135860(25056376)1525.463
clang 199.927.91522567820128419161478.956
watcom 387.326.42822242918111869342199.402
dmc 371.925.1212246058090407741560.336
pcc 289.932.3194367962780334191176.030
COINS 384.128.0187006550059908941335.854
Bcc 5.5 582.239.41909259047101112231692.216
PellesC 429.330.4146966679473179651241.080
TinyCC 511.449.96621310585857544021063.352
(補足) ↓good↓good↓good↓good↑good↑good


たったこれだけだし、 測定(実行)環境はザルで実行毎の誤差からすれば 細かい数値みちゃうとまずいだろう。 特に浮動少数演算関係のオプション設定が適切でないだろうで 設定かえたら(Whetstoneとか)結果変わってきそうな気もする. (c標準ライブラリも計測範囲で使われてるので それの影響もどう据えるかにもよるし)。 が、おおざっぱに、vcが1番よさそうで次gcc系、 後は混戦で、ケツがTinyCCって感じか。
今回以外の例を思えば vcが抜きでてるとはいいきれないけれど. (Intel C/C++気になるけど懐的に手が出せないのでVCで満足しとく)

vc9sp1 64 の結果は CPUが64bit CPUなので、コンパイラの比較ってよりCPUの比較になるけど、 64ビットOS用にコンパイルするだけで、 結構違いがでる処理もある、という例になってるかも、で(代わり映えしない処理もあるけど)。

バイリニア拡大の結果は、 64ビット整数の利用頻度が少なかろうと 汎用レジスタが増えたことによるメリットが効いている例だろう (あとSSE2も関係あるか? 追記:というか、汎用レジスタどうこうでなく、 まさにSSE2になったことが効いているよう。 clangの結果がいいのも、SSE?なコードが生成されているためのよう (clangのほうはsseしてない) )。 32bitでもVCが結構速くなる例で、結構かたよった代物だろうけど... (この手の画像処理なんかは普通からすれば特殊な扱いになるのかも. 速度/ハードを気にしだすと生のCだけで済まさないだろうし)

感想としては llvm(-gcc)は(いろいろミスして勘違いしまくりだったけれど)ものによっては非常に速くなったりでびっくりで早く使い易くなってほしく、 Clangはllvmをしなくても(?)これだけの性能あっていろいろ期待、 PCC は速度は思ってたよりはいいかも、だけど(Win版は)思ったより安定してなさそう、 PellesC はそんなもんだろう、 TinyCC は実行時速度じゃなくてコンパイル速度優先で(-benchなんてオプションがあるくらいだし)スクリプト言語的にCソースを扱うような方向があるようなので 実行時速度はこの程度の差ならokなんだろう、 COINSは悪くないのだけど期待しすぎてたため (ひょっとするとあまりx86系向きじゃないのかもだけど) ちょっと哀しい気分、てとこか。

※追記:cygwin-gcc、測定ミスでやりなおしたけど、gcc3.4の範囲なのでたいして値はかわってない(でもWhetstoneは結構ちがうか。浮動小数点はオプション設定しだいだろうでデフォルトがいい状態だったのかも)。 もともとcygwin使ったCOINSの結果と比較するのにmingwのでいいか微妙な気もして追加したものなので(ただの計算だけだとそう違わないだろうけど). あと最初の4つのテストで cygwin-gccとCOINSは win-apiでなくtime.hを用いて計測しているため若干条件が違う、とも.

※追記:安易インタプリタの結果は、clang対策での修正前にくらべ、どれも4,5秒短くなっている. isalphaやtoupperを手抜きなマクロに置き換えただけだが、結構ライブラリのオーバーヘッドがあったということか. テーブル参照より単純比較のほうが速いのかスレッド関係やロケール対策がらみなのかはしらないけれど。

※追記: llvm-gcc のやり直しにおいて、Bilinear拡大は最適化されすぎて計算がなくなってしまったため、そうならないよう、若干ソースを修正(計測時間外での対処)した.
また、Dhrystone2.1の結果も最適化されるぎている可能性があるかも(大丈夫?).
(dhry_1.c dhry_2.c を1つのソースにした場合のvc64も結構はやかったので 全体最適化としてはありなのかも?)

※追記:llvm-gcc 同様に clangも -emit-llvm で生成すると速くなる模様 (例えばbilinear拡大のは 100.9 に). が、どうも何かミスっていて正常終了できないハングするexeが作られるためちゃんと試せていない.(追記:スタックフレームポインタを保持してるはずの ebp を退避せずに汎用レジスタ的に破壊して使うコードができてる模様) (4/23追記: 試したのはbilinearのだけだが、最近のclang(1.5 trunk 102038)で試したら修正されてるようで動作)


コンパイル不具合のメモ

  • pcc は #include で、#より前に空白が置かれていると、エラーになる場合があった.
  • 行頭に#を置けばok. #の後ろに空白が入るのはok.
  • pcc で、初期化していないグローバル or static 変数でハングする場合あり(必ずではなく).
  • char buf[10] ={0}; な感じに初期値を入れてしまうことで回避できた.
  • COINSでdhrystoneコンパイル時、 gccのヘッダを流用しているため?か、
  • stdio.hヘッダにあるstatic inlineになっていないinline関数(fget?の内部関数)が 複数のobjでincludeされてると、リンクで名前の衝突が発生. (実際に必要としたソースは1つだったので、includeを調整して回避).
  • COINS, 正常なtolower(c)で')'がらみでエラー. (interpreter_smp:マクロでtoupperを乗っ取って回避).

(とかいたけど、coinsはちゃんと環境できていない可能性もあり)