web-dev-qa-db-ja.com

Clang対GCC-より良いバイナリを生成しますか?

現在GCCを使用していますが、最近Clangを発見し、切り替えを検討しています。ただし、1つの決定要因があります-生成するバイナリの品質(速度、メモリフットプリント、信頼性)-gcc -O3_が1%高速で実行するか、メモリを1%削減するバイナリを生成できる場合、それは重大な問題です。

ClangはGCCよりも優れたコンパイル速度とコンパイル時のメモリフットプリントを誇りますが、結果としてコンパイルされたソフトウェアのベンチマーク/比較に興味があります。

222
SF.

以下は、GCC 4.7.2およびC++ 3.2のClang 3.2での私の狭い所見にもかかわらず、最新の発見です。

更新:GCC 4.8.1 v以下にclang 3.3比較を追加

更新:GCC 4.8.2 v clang 3.4比較がそれに追加されます。

私はGCCとClangの両方でLinux用に構築されたOSSツールと、Windows用のMicrosoftのコンパイラーを保守しています。ツールcoanは、C/C++ソースファイルとそのようなコードラインのプリプロセッサおよびアナライザーです。再帰降下解析およびファイル処理に関する計算プロファイルを専攻しています。開発ブランチ(これらの結果が関係する)は、現在約90個のファイルで約11K LOCを構成しています。現在、ポリモーフィズムとテンプレートが豊富なC++でコーディングされていますが、ハッキングされたCのそれほど遠くない過去によって、多くのパッチでまだ動かされていません。移動セマンティクスは明示的に活用されていません。シングルスレッドです。 「アーキテクチャ」の大部分はToDoのままですが、それを最適化することに真剣な努力はしていません。

3.2よりも前のClangは、優れたコンパイル速度と診断にも関わらず、C++ 11標準サポートがcoanが行使する点で現代のGCCバージョンに遅れていたため、実験的なコンパイラとしてのみ採用しました。 3.2では、このギャップは埋められました。

現在のcoan開発プロセス用の私のLinuxテストハーネスは、1ファイルパーサーテストケース、数千のファイルを消費するストレステスト、および1 K未満のファイルを消費するシナリオテストの混合でおよそ70Kのソースファイルを処理します。テスト結果を報告するだけでなく、ハーネスはcoanで消費されたファイルと実行時間の合計を蓄積して表示します(各coanコマンドラインをLinuxのtimeコマンドに渡し、報告された数値をキャプチャして加算します)。 0の測定可能な時間を要するテストの数はすべて合計0になりますが、そのようなテストの寄与はごくわずかであるという事実によって、タイミングはお世辞になります。タイミング統計は、make checkの最後に次のように表示されます。

coan_test_timer: info: coan processed 70844 input_files.
coan_test_timer: info: run time in coan: 16.4 secs.
coan_test_timer: info: Average processing time per input file: 0.000231 secs.

GCC 4.7.2とClang 3.2の間でテストハーネスのパフォーマンスを比較しました。コンパイラーを除くすべての条件は同じです。 Clang 3.2の時点で、GCCがコンパイルするコード領域とClangの代替との間でプリプロセッサを区別する必要がなくなりました。各ケースで同じC++ライブラリ(GCC)にビルドし、同じターミナルセッションですべての比較を連続して実行しました。

リリースビルドのデフォルトの最適化レベルは-O2です。また、-O3でビルドを正常にテストしました。各構成を3回連続してテストし、3つの結果を平均して、次の結果を得ました。データセル内の数値は、〜70Kの各入力ファイル(出力および診断の読み取り、解析、書き込み)を処理するためにcoan実行可能ファイルが消費するマイクロ秒の平均数です。

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 231 | 237 |0.97 |
----------|-----|-----|-----|
Clang-3.2 | 234 | 186 |1.25 |
----------|-----|-----|------
GCC/Clang |0.99 | 1.27|

特定のアプリケーションには、コンパイラの長所または短所に対して不公平に作用する特性がある可能性が非常に高くなります。厳密なベンチマークでは、多様なアプリケーションが使用されます。それを念頭に置いて、これらのデータの注目すべき機能は次のとおりです。

  1. -O3最適化はGCCにわずかに有害でした
  2. -O3最適化はClangにとって非常に有益でした
  3. -O2最適化では、GCCはひげだけでClangよりも高速でした
  4. -O3最適化では、ClangはGCCよりも非常に高速でした。

2つのコンパイラのさらに興味深い比較は、これらの発見の直後に偶然に発生しました。 Coanはスマートポインターを自由に使用しており、その1つはファイルの処理に重点を置いています。この特定のスマートポインター型は、コンパイラーの差別化のために以前のリリースでtypedefされており、構成されたコンパイラーがその使用を十分にサポートしている場合はstd::unique_ptr<X>、そうでなければstd::shared_ptr<X>になりました。 std::unique_ptrへのバイアスは、これらのポインターが実際に転送されたため、愚かでしたが、C++ 11バリアントが私にとって目新しいものだった時点で、std::unique_ptrstd::auto_ptrを置き換えるためのフィッターオプションのように見えました。

これと同様の差別化に対するClang 3.2の継続的な必要性を評価するための実験的ビルドの過程で、std::shared_ptr<X>をビルドするつもりであったときに、誤ってstd::unique_ptr<X>をビルドし、デフォルトの-O2最適化で結果の実行可能ファイルが私が見た中で最速で、時には184ミリ秒を達成しました。入力ファイルごと。ソースコードのこの1つの変更により、対応する結果は次のようになりました。

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.7.2 | 234 | 234 |1.00 |
----------|-----|-----|-----|
Clang-3.2 | 188 | 187 |1.00 |
----------|-----|-----|------
GCC/Clang |1.24 |1.25 |

ここでの注意点は次のとおりです。

  1. どちらのコンパイラも、-O3最適化の恩恵をまったく受けません。
  2. Clangは、最適化の各レベルで同じくらい重要なこととしてGCCに勝っています。
  3. GCCのパフォーマンスは、スマートポインタータイプの変更によってわずかに影響されます。
  4. Clangの-O2パフォーマンスは、スマートポインタータイプの変更によって重要な影響を受けます。

スマートポインタータイプの変更の前後で、Clangは-O3最適化で大幅に高速なcoan実行可能ファイルをビルドできます。また、そのポインタータイプが最適な場合、-O2および-O3で同等に高速な実行可能ファイルをビルドできます-std::shared_ptr<X> - 仕事で。

私がコメントする能力がないという明らかな質問は、whyです。Clangは、アプリケーションで25%-O2の高速化を見つけることができるはずです。頻繁に使用されるスマートポインタータイプは一意から共有に変更されますが、GCCは同じ変更に対して無関心です。また、Clangの-O2最適化が、スマートポインターの選択の知恵に対して非常に大きな感度を持っているという発見を応援するか、ブーイングするかどうかもわかりません。

更新:GCC 4.8.1 v clang 3.

対応する結果は次のとおりです。

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.1 | 442 | 443 |1.00 |
----------|-----|-----|-----|
Clang-3.3 | 374 | 370 |1.01 |
----------|-----|-----|------
GCC/Clang |1.18 |1.20 |

4つの実行可能ファイルすべてが1つのファイルを処理するのに以前よりはるかに長い平均時間を要するという事実は、notは最新のコンパイラのパフォーマンスを反映しています。これは、テストアプリケーションの後の開発ブランチが、その間に多くの構文解析の洗練度を引き継ぎ、その代価を払っているという事実によるものです。重要なのは比率のみです。

現在、注目すべき点は目新しいものではありません。

  • GCCは-O3最適化とは無関係です
  • clangは-O3最適化の恩恵をわずかに受けます
  • clangは、最適化の各レベルで同様に重要なマージンでGCCを上回っています。

これらの結果をGCC 4.7.2およびclang 3.2の結果と比較すると、GCCが各最適化レベルでclangのリードの約4分の1を引き戻していることがわかります。しかし、その間にテストアプリケーションが大幅に開発されているため、これをGCCのコード生成の追いつきに自信を持って帰属させることはできません。 (今回は、タイミングが取得されたアプリケーションのスナップショットに注意し、それを再び使用することができます。)

更新:GCC 4.8.2 v clang 3.4

GCC 4.8.1 v Clang 3.3のアップデートを終了しましたが、さらなるアップデートのために同じコアスナップショットに固執すると言いました。しかし、代わりに、そのスナップショット(rev。301)andでテストすることにしました。テストスイート(rev。619)に合格した最新の開発スナップショットで。これにより、結果に少し経度が与えられ、別の動機がありました。

私の最初の投稿は、スピードのためにコアを最適化する努力をしていないことを指摘しました。これは、revの時点でまだ当てはまりました。 301.しかし、タイミングテスト装置をコアテストハーネスに組み込んだ後、テストスイートを実行するたびに、最新の変更によるパフォーマンスへの影響に直面しました。多くの場合、それは驚くほど大きく、傾向は機能性の向上に値すると思われるよりも急激にマイナスであることがわかりました。

回転数308テストスイートの入力ファイルあたりの平均処理時間は、ここに最初に投稿してから2倍以上になりました。その時点で、パフォーマンスを気にしないという10年間の方針をUターンしました。集中的な改訂の中で、最高619のパフォーマンスは常に考慮事項であり、それらの多くは、基本的に高速な行でキーロードベアラーを完全に書き直しました(ただし、非標準のコンパイラ機能は使用していません)。このUターンに対する各コンパイラの反応を見るのは興味深いでしょう。

以下は、最新の2つのコンパイラーのrev.301のビルドで使用されているタイミングマトリックスです。

coan-rev.301 results

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 428 | 428 |1.00 |
----------|-----|-----|-----|
Clang-3.4 | 390 | 365 |1.07 |
----------|-----|-----|------
GCC/Clang | 1.1 | 1.17|

ここでのストーリーは、GCC-4.8.1およびClang-3.3からわずかに変更されています。 GCCの表示は些細なことです。 Clangの方が些細なことです。ノイズがこれをうまく説明できます。 Clangは、-O2および-O3マージンによってまだ先に出ています。これらのマージンは、ほとんどのアプリケーションでは重要ではありませんが、非常に重要です。

そして、ここに回転のためのマトリックスがあります。 619。

coan-rev.619 results

          | -O2 | -O3 |O2/O3|
----------|-----|-----|-----|
GCC-4.8.2 | 210 | 208 |1.01 |
----------|-----|-----|-----|
Clang-3.4 | 252 | 250 |1.01 |
----------|-----|-----|------
GCC/Clang |0.83 | 0.83|

301と619の数字を並べてみると、いくつかの点がはっきりとわかります。

  • 私はより高速なコードを書くことを目指していましたが、両方のコンパイラーは私の努力を強調しています。しかし:

  • GCCは、Clangよりもはるかに寛大にこれらの努力を払い戻します。 -O2最適化でClangの619ビルドは301ビルドより46%高速です:-O3でClangの改善は31%です。良いですが、各最適化レベルでGCCの619ビルドは301の2倍以上高速です。

  • GCCは、Clangの以前の優位性を覆します。各最適化レベルで、GCCはClangを17%上回るようになりました。

  • -O3最適化からGCCよりも多くのレバレッジを得る301ビルドのClangの機能は、619ビルドでなくなりました。どちらのコンパイラも-O3から有意な利益を得ることはありません。

この運勢の逆転には、clang 3.4自体のビルドが誤って(ソースからビルドしたため)誤って作成したのではないかと疑っていました。それで、ディストリビューションの在庫Clang 3.3で619テストを再実行しました。結果は実際には3.4と同じでした。

Uターンへの反応に関して:ここでの数字については、Clangは、CCCが助けを与えていないときにC++コードを圧縮する速度がGCCよりもはるかに優れていました。私が支援することに心を向けたとき、GCCはClangよりもはるかに良い仕事をしました。

私はその観察を原則に昇格させませんが、「どのコンパイラーがより良いバイナリーを生成するか」というレッスンを取ります。答えは相対的であるテストスイートを指定したとしても、バイナリのタイミングを計るだけの明確な問題ではないという質問です。

あなたのより良いバイナリは最速のバイナリですか、それとも安価に作成されたコードを最もよく補うものですか?または、高価な速度よりも保守性と再利用を優先する細工されたコードを最もよく補いますか?それは、バイナリを生成する動機の性質と相対的な重み、およびそのための制約に依存します。

いずれにせよ、「最高の」バイナリの構築に深く関心がある場合は、コンパイラの連続した反復が、コードの連続した反復にわたって「最高の」というアイデアをどのように実現するかをチェックし続ける必要があります。

227
Mike Kinghan

Phoronixはこれについていくつかの ベンチマーク を行いましたが、それは数か月前のClang/LLVMのスナップショットバージョンに関するものです。その結果、物事は多かれ少なかれプッシュでした。 GCCもClangも、すべての場合において明確に優れているわけではありません。

最新のClangを使用するため、少し関連性が低いかもしれません。繰り返しになりますが、GCC 4.6は、明らかにCore 2とi7で 主要な最適化 を持つ予定です。

Clangの高速なコンパイル速度は、元の開発者にとってより良いものになると考えています。そして、コードを世に送り出すと、Linux distro/BSD/etcになります。エンドユーザーは、高速なバイナリにGCCを使用します。

47
Nietzche-jou

Clangがコードをより速くコンパイルするという事実は、結果のバイナリの速度ほど重要ではないかもしれません。ただし、これは 一連のベンチマーク です。

15
mcandre

生成されるバイナリの速度に関して、GCC 4.8とclang 3.3の全体的な違いはほとんどありません。ほとんどの場合、両方のコンパイラによって生成されたコードは同様に実行されます。これら2つのコンパイラーのどちらも、他のコンパイラーを支配しません。

GCCとclangの間に大きなパフォーマンスギャップがあることを示すベンチマークは偶然です。

プログラムのパフォーマンスは、コンパイラの選択によって影響を受けます。開発者または開発者グループがGCCのみを使用している場合、プログラムはclangよりもGCCでわずかに高速で実行され、その逆も同様です。

開発者の観点から見ると、GCC 4.8+とclang 3.3の顕著な違いは、GCCに-Ogコマンドラインオプションがあることです。このオプションは、デバッグに干渉しない最適化を有効にするため、たとえば、正確なスタックトレースを常に取得できます。 clangにこのオプションがないため、一部の開発者にとってclangを最適化コンパイラとして使用するのが難しくなります。

12
user811773

これを判断する唯一の方法は、試すことです。 FWIW通常のgcc 4.2(かなり多くのSSEを使用するx86-64コードの場合)に比べて、AppleのLLVM gcc 4.2を使用すると、コードベースが異なるYMMVを使用して、本当に良い改善を見ました。あなたがx86/x86-64で作業していて、最後の数パーセントを本当に気にかけていると仮定すると、IntelのICCも試してみる必要があります。これはgccに勝る場合が多いためです。そしてそれを試してください。

9
Paul R

Gcc 5.2.1とclang 3.6.2で私が指摘した特異な違いは、次のようなクリティカルループがある場合です。

for (;;) {
    if (!visited) {
        ....
    }
    node++;
    if (!*node) break;
  }

次に、gccは-O3または-O2でコンパイルするときに、投機的にループを8回展開します。 Clangはまったく展開しません。試行錯誤を通して、プログラムデータの特定のケースでは、適切な展開量は5なので、gccオーバーシュートとclangアンダーシュートが発生しました。ただし、オーバーショットはパフォーマンスに悪影響を与えるため、gccのパフォーマンスはここでさらに悪化しました。

展開の違いが一般的な傾向であるか、または私のシナリオに固有のものである場合、私は考えを持たない

しばらく前に 少数のガベージコレクター を書いて、Cでのパフォーマンスの最適化について詳しく学びました。得られた結果は、clangをわずかに好むほど十分に頭にあります。特にガベージコレクションは、主にメモリの追跡とコピーに関するものです。

結果は次のとおりです(秒単位の数値):

+---------------------+-----+-----+
|Type                 |GCC  |Clang|
+---------------------+-----+-----+
|Copying GC           |22.46|22.55|
|Copying GC, optimized|22.01|20.22|
|Mark & Sweep         | 8.72| 8.38|
|Ref Counting/Cycles  |15.14|14.49|
|Ref Counting/Plain   | 9.94| 9.32|
+---------------------+-----+-----+

これはすべて純粋なCコードであり、C++コードをコンパイルする際のコンパイラーのパフォーマンスについては主張していません。

Ubuntu 15.10、x86.64、およびAMD Phenom(tm)II X6 1090Tプロセッサー。

7

基本的に言えば、答えは次のとおりです。さまざまな種類のアプリケーションに焦点を当てた多くのベンチマークがあります。

私のアプリのベンチマークは、gcc> icc> clangです。

まれなIOがありますが、多くのCPUフロートおよびデータ構造操作。

コンパイルフラグは-Wall -g -DNDEBUG -O3です。

https://github.com/zhangyafeikimi/ml-pack/blob/master/gbdt/profile/benchmark

4
kimi