1. ホーム

[解決済み】Clang vs GCC - どちらがより速いバイナリを生成するか?[クローズド]

2022-05-05 17:17:03

質問

現在GCCを使っていますが、最近Clangを発見して乗り換えを思案中です。しかし、1つの決め手があります - 生成されるバイナリの品質(速度、メモリフットプリント、信頼性) - もし gcc -O3 Clangのバイナリはより多くのメモリを消費するか、コンパイラのバグで失敗します。

ClangはGCCよりも優れたコンパイル速度と低いコンパイル時のメモリフットプリントを誇りますが、私はコンパイルされたソフトウェアのベンチマークや比較にとても興味があります - 既存のリソースやあなた自身のベンチマークを紹介してもらえますか?

どのように解決するのですか?

GCC 4.7.2を使用して、狭い範囲ではありますが、私の最新の知見をご紹介します。 とClang 3.2 for C++を使用して、狭い範囲ではありますが、私の最新の知見を紹介します。

UPDATE: GCC 4.8.1 v clang 3.3 の比較を以下に追記しました。

UPDATE: GCC 4.8.2 v clang 3.4 比較を追記しました。

私は、GCCとClangの両方を使用してLinux用にビルドされたOSSツールを保守しています。 とMicrosoftのWindows用コンパイラを使用しています。そのツールは コアン プリプロセッサー C/C++のソースファイルやそのコードラインの解析に使用されます。 計算プロファイルは再帰的降下構文解析とファイルハンドリングに特化しています。 開発ブランチ(この結果に関連する) 現在、約90ファイル、約11K LOCで構成されています。コード化されています。 ポリモーフィズムとテンプレートが豊富なC++で、現在もなお C言語をハックして作ったという、それほど遠くない過去から多くのパッチを受け継いでいます。 手先のセマンティクスは明示的に悪用されることはない。シングルスレッドである。I 最適化のための本格的な努力はしていません。 は、ほとんどToDoのままです。

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

私のLinuxテストハーネスは、現在のcoanの開発プロセスのために、およそ 1ファイルパーサーのテストケースと、ストレステストケースとが混在しており、ソースファイルは70Kにも及びます。 テストは1000ファイル、シナリオテストは1,000ファイルを消費します。

テスト結果を報告するだけでなく、ハーネスに蓄積され 消費されたファイルの合計と、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の時点では。 Clang 3.2ではプリプロセッサによるコードの区別が不要になりました。 GCCがコンパイルするトラクトとClangが代替するトラクト。私は それぞれ同じC++ライブラリ(GCCのもの)を使用し、すべての比較を実行しました。 同じターミナルセッションで連続的に

私のリリースビルドの最適化レベルのデフォルトは-O2です。また O3でのビルドのテストに成功しました。各設定を3回ずつテストしました。 を2回連続で行い、3回の結果を平均化したところ、以下のようになりました。 の結果です。データセル内の数値は、平均値です。 coan実行ファイルが各処理に消費したマイクロ秒は次のとおりです。 入力ファイル(読み込み、パース、出力と診断の書き込み)は約70K。

          | -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はスマートポインタを自由に使っており ファイル操作に多用されています。この特殊な スマート・ポインターの型は、以前のリリースでは、型定義されていた。 コンパイラの区別のために std::unique_ptr<X> もし として使用するためのサポートが十分に成熟しているコンパイラです。 であり、そうでなければ std::shared_ptr<X> . に偏っている。 std::unique_ptr は というのも、これらのポインタは実際に転送されたものだからです。 しかし std::unique_ptr を置き換えるための適切なオプションのように見えました。 std::auto_ptr C++11の亜種が私にとって斬新だった時点で。

Clang 3.2の継続的な必要性を評価するための実験的なビルドの過程で というのは、このような差別化のために、私は誤って std::shared_ptr<X> をビルドするつもりだったのが std::unique_ptr<X> , で、出来上がった実行ファイルを観察して驚いたのですが、デフォルトの-O2 の最適化は、私が見た中で最速で、時には184 msec.である。このソースコードの変更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 は Coan実行ファイルを-O3最適化で大幅に高速化することができ、また の場合、-O2や-O3でも同様に高速な実行ファイルを構築することができます。 ポインタ型が最適です。 std::shared_ptr<X> - ということになります。

私にはコメントする能力がない明白な疑問は なぜ Clang は、私のアプリケーションで 25% -O2 のスピードアップを見つけることができるはずです。 使用頻度の高いスマートポインタの型をユニークからシェアードに変更した。 一方、GCCは同じ変更に無関心です。また、私は Clang の -O2 最適化には、以下のような特徴があることがわかりました。 スマート・ポインタの選択の賢明さについて、このような大きな感度を持つようになりました。

UPDATE: GCC 4.8.1 v clang 3.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ファイル ではなく は、最新のコンパイラの性能を反映しています。これは テストアプリケーションの後期開発ブランチは、多くのことを引き受けています。 その間に高度な解析が行われ、その代償として速度が向上しました。比率は は重要です。

今注目している点は、目を見張るような斬新さはない。

  • GCCは-O3最適化に無頓着である。
  • clangは-O3最適化によってごくわずかな利益を得ることができます。
  • clangは、各最適化レベルにおいて、同様に重要なマージンをもってGCCに勝っている。

これらの結果をGCC 4.7.2とclang 3.2の結果と比較すると、以下の点が顕著に現れています。 GCCは、各最適化レベルにおいて、clangのリードの約4分の1を取り戻したのです。しかし この間、テストアプリケーションの開発はかなり進んでいるので これは、GCCのコード生成のキャッチアップによるものだと自信を持って言えます。 (今回、私はこのタイミングを得たアプリケーションのスナップショットを記録しています。 また使えると思います)。

UPDATE: GCC 4.8.2 v clang 3.4

GCC 4.8.1 v. Clang 3.3 のアップデートを終了し、次のように述べました。 今後の更新は、同じcuan snaphotにこだわります。しかし、私は そのスナップショット(rev. 301)でテストする代わりに 最新の開発版 は、そのテストスイートをパスしたスナップショット(rev. 619)を持っています。これによって、結果が という、もうひとつの動機がありました。

私が最初に投稿したのは、coanを最適化する努力を全くしていないことです。 スピード これはrev.の時点でも同じでした。301. しかし、私が構築した後 coanのテストハーネスにタイミング装置を組み込んだところ、テストスイートを実行するたびに 今回の変更による性能への影響を目の当たりにしたのです。その結果 また、その傾向も、「震災前」「震災後」「震災後」よりも急なマイナスになっています。 機能性の向上に見合うだけの価値があると感じたからです。

Rev. 308では、テストスイートの入力ファイル1つあたりの平均処理時間は、1.5倍になっていました。 このページに最初に投稿したときと比べると、2倍以上になっています。このとき、私は 10年間、性能にはこだわらないという方針をUターンさせたのです。集中的に行った 619までの一連のリビジョンでは、パフォーマンスが常に考慮され、また その多くは、主要なロードベアラーを根本的に書き換えることに終始しています。 そのために非標準のコンパイラ機能を使用することはありません)。このような状況に対して、各コンパイラがどのような反応を示すか、興味深いところです。 Uターンです。

以下は、今やおなじみとなった、最新の2つのコンパイラによるrev.301のビルドのタイミング・マトリックスです。

coan - rev.301 の結果

          | -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 のマージンは、ほとんどの場合、重要ではありません。 しかし、一部のアプリケーションでは重要です。

そして、こちらがRev.619のマトリックスです。

coan - rev.619の結果

          | -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%も勝っています。

  • 301ビルドでClangがGCCよりもレバレッジを効かせることができるのは、以下からです。 -O3 最適化 が619ビルドでは消えています。どちらのコンパイラーも -O3 .

この逆転劇に十分驚きました。 clang 3.4 自体を誤って低調にビルドしてしまったのかもしれません。 をソースから作成しました。) そこで、私のディストロの純正 Clang 3.3 で 619 テストを再実行しました。その結果 結果は3.4と実質的に同じでした。

では、Uターンの反応についてですが。ここでの数字では、Clangはずっとうまくいっています。 GCCは、私のC++コードから速度を引き出すのにGCCより優れています。 ということです。私が手助けすることに心を砕いたとき、GCCはClangよりもずっと良い仕事をしました。

私は、この観察を原則とはしませんが どのコンパイラがより良いバイナリを生成するか? テストスイートを指定しても、その答えが相対的なものでなければならない。 バイナリのタイミングを計ればいいというものではありません。

より良いバイナリーは最速のバイナリーなのか、それとも 安価に作られたコードを補うことができますか?あるいは エクスペンシブ スピードよりも保守性・再利用性を優先して作られたコード?それは バイナリを作成する動機の性質と相対的な重み、そして 制約がある場合。

そして、いずれにせよ、もしあなたが最高のバイナリをビルドすることに深い関心を抱いているのなら を、歴代のコンパイラがどのように実現したかをチェックし続ける必要があります。 という考え方は、コードの反復を重ねるうちに、最良のものとなっていきます。