wake-up-neo.net

Clang vs GCC - was produziert bessere Binaries?

Ich verwende derzeit GCC, habe Clang jedoch kürzlich entdeckt und überlege, ob ich wechseln soll. Es gibt jedoch einen entscheidenden Faktor - Qualität (Geschwindigkeit, Speicherbedarf, Zuverlässigkeit) der erzeugten Binärdateien - wenn gcc -O3kann eine Binärdatei erzeugen, die 1% schneller ausgeführt wird oder 1% weniger Speicher benötigt, ist ein Deal-Breaker.

Clang bietet bessere Kompiliergeschwindigkeiten und einen geringeren Speicherbedarf beim Kompilieren als GCC, aber ich bin wirklich an Benchmarks/Vergleichen der resultierenden kompilierten Software interessiert - können Sie mich auf einige verweisen oder Ihre Erfahrungen beschreiben?

224
SF.

Hier sind einige aktuelle, wenn auch enge Ergebnisse von mir mit GCC 4.7.2 und Clang 3.2 für C++.

PDATE: GCC 4.8.1 v clang 3.3 Vergleich siehe unten.

PDATE: GCC 4.8.2 v clang 3.4 Vergleich wird angehängt.

Ich verwalte ein OSS-Tool, das für Linux sowohl mit GCC als auch mit Clang und mit dem Microsoft-Compiler für Windows erstellt wurde. Das Tool coan ist ein Präprozessor und Analysator für C/C++ - Quelldateien und Codelines von C/C++. Der Entwicklungszweig (auf den sich diese Ergebnisse beziehen) umfasst derzeit ca. 11 KB LOC in ca. 90 Dateien. Es ist jetzt in C++ codiert, das reich an Polymorphismus und Vorlagen ist, aber immer noch in vielen Patches verstrickt ist, aufgrund seiner nicht allzu fernen Vergangenheit in zusammengebrochenem C. Die Bewegungssemantik wird nicht ausdrücklich ausgenutzt. Es ist Single-Threaded. Ich habe keine ernsthaften Anstrengungen unternommen, um es zu optimieren, während die "Architektur" so weitgehend ToDo bleibt.

Ich habe Clang vor Version 3.2 nur als experimentellen Compiler eingesetzt, da seine C++ 11-Standardunterstützung trotz seiner überlegenen Kompilierungsgeschwindigkeit und -diagnose der aktuellen GCC-Version in der von coan geübten Hinsicht hinterherhinkte. Mit 3.2 wurde diese Lücke geschlossen.

Mein Linux-Testkit für aktuelle Coan-Entwicklungsprozesse umfasst ca. 70.000 Quelldateien in einer Mischung aus Parser-Testfällen mit einer Datei, Stresstests mit Tausenden von Dateien und Szenarientests mit <1.000 Dateien. Das Kabel sammelt nicht nur die Testergebnisse, sondern zeigt auch die Gesamtmenge der in coan verbrauchten Dateien und die in coan verbrauchte Laufzeit an (es übergibt einfach jede coan-Befehlszeile an den Linux-Befehl time und erfasst und summiert die gemeldeten Zahlen). Das Timing wird durch die Tatsache geschmeichelt, dass sich jede Anzahl von Tests, die 0 messbare Zeit benötigen, zu 0 summiert, aber der Beitrag solcher Tests ist vernachlässigbar. Die Timing-Statistiken werden am Ende von make check folgendermaßen angezeigt:

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.

Ich verglich die Leistung des Testgeschirrs zwischen GCC 4.7.2 und Clang 3.2, wobei alle Dinge außer den Compilern gleich waren. Ab Clang 3.2 ist keine Präprozessor-Unterscheidung mehr zwischen den von GCC kompilierten Codebereichen und Clang-Alternativen erforderlich. Ich habe jeweils dieselbe C++ - Bibliothek (GCCs) erstellt und alle Vergleiche nacheinander in derselben Terminalsitzung ausgeführt.

Die Standardoptimierungsstufe für meinen Release-Build ist -O2. Ich habe auch Builds bei -O3 erfolgreich getestet. Ich habe jede Konfiguration dreimal hintereinander getestet und den Durchschnitt der drei Ergebnisse mit den folgenden Ergebnissen ermittelt. Die Anzahl in einer Datenzelle ist die durchschnittliche Anzahl von Mikrosekunden, die die ausführbare Coan-Datei benötigt, um jede der ~ 70K-Eingabedateien zu verarbeiten (Lesen, Parsen und Schreiben, Ausgabe und Diagnose).

          | -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|

Es ist sehr wahrscheinlich, dass eine bestimmte Anwendung Merkmale aufweist, die die Stärken oder Schwächen eines Compilers ungerecht spielen. Strenges Benchmarking verwendet verschiedene Anwendungen. In diesem Sinne sind die bemerkenswerten Merkmale dieser Daten:

  1. -O3-Optimierung wirkte sich geringfügig nachteilig auf GCC aus
  2. -O3-Optimierung war für Clang von großem Vorteil
  3. Bei der -O2-Optimierung war GCC nur um einen Whisker schneller als Clang
  4. Bei der -O3-Optimierung war Clang bedeutend schneller als GCC.

Ein weiterer interessanter Vergleich der beiden Compiler ergab sich aus Versehen kurz nach diesen Feststellungen. Coan setzt großzügig intelligente Zeiger ein, und einer davon wird im Umgang mit Dateien stark beansprucht. Dieser spezielle Smart-Pointer-Typ wurde in früheren Releases aus Gründen der Compiler-Differenzierung als std::unique_ptr<X> eingegeben, wenn der konfigurierte Compiler seine Verwendung als solcher ausreichend ausgereift unterstützt, und ansonsten als std::shared_ptr<X>. Die Neigung zu std::unique_ptr war töricht, da diese Zeiger tatsächlich verschoben wurden, aber std::unique_ptr sah zu einem Zeitpunkt, als die C++ 11-Varianten neu waren, wie die richtige Option zum Ersetzen von std::auto_ptr aus mir.

Während experimenteller Builds, um Clangs anhaltenden Bedarf an dieser und ähnlicher Differenzierung zu messen, habe ich versehentlich std::shared_ptr<X> erstellt, als ich beabsichtigte, std::unique_ptr<X> zu erstellen, und war überrascht zu beobachten, dass die resultierende ausführbare Datei war mit der Standard -O2-Optimierung die schnellste, die ich je gesehen hatte und erreichte manchmal 184 ms. pro Eingabedatei. Mit dieser einen Änderung am Quellcode waren die entsprechenden Ergebnisse diese;

          | -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 |

Die wichtigen Punkte hier sind:

  1. Keiner der beiden Compiler profitiert jetzt von der -O3-Optimierung.
  2. Clang schlägt GCC auf jeder Ebene der Optimierung gleichermaßen.
  3. Die Leistung von GCC wird durch die Änderung des Smart-Pointer-Typs nur unwesentlich beeinträchtigt.
  4. Die -O2-Leistung von Clang wird durch die Änderung des Smart-Pointer-Typs erheblich beeinträchtigt.

Vor und nach der Änderung des Smart-Pointer-Typs kann Clang eine wesentlich schnellere ausführbare Coan-Datei bei -O3-Optimierung und eine ebenso schnellere ausführbare Datei bei -O2 und -O3 erstellen, wenn dieser Zeigertyp der beste ist - std::shared_ptr<X> - für den Job.

Eine offensichtliche Frage, die ich nicht beantworten kann, ist , warum Clang in der Lage sein sollte, eine 25% -O2-Beschleunigung in meiner Anwendung zu finden, wenn a Der häufig verwendete Smart-Pointer-Typ wird von "Unique" in "Shared" geändert, während "GCC" für dieselbe Änderung gleichgültig ist. Ich weiß auch nicht, ob ich die Entdeckung, dass Clangs -O2-Optimierung eine so große Sensibilität für die Weisheit meiner Smart-Pointer-Entscheidungen birgt, anfeuern oder ausspähen sollte.

PDATE: GCC 4.8.1 v clang 3.

Die entsprechenden Ergebnisse sind nun:

          | -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 |

Die Tatsache, dass alle vier ausführbaren Dateien im Durchschnitt viel länger brauchen als zuvor, um eine Datei zu verarbeiten , wirkt sich nicht auf die Leistung der neuesten Compiler aus . Dies liegt an der Tatsache, dass der spätere Entwicklungszweig der Testanwendung in der Zwischenzeit eine Menge Parsing-Raffinesse angenommen hat und sich schnell bezahlt macht. Nur die Verhältnisse sind signifikant.

Die Punkte, die jetzt zu beachten sind, sind nicht auffallend neu:

  • GCC ist gegenüber der -O3-Optimierung gleichgültig
  • clang profitiert nur unwesentlich von der -O3-Optimierung
  • clang schlägt GCC auf jeder Optimierungsstufe um einen ähnlich wichtigen Punkt.

Vergleicht man diese Ergebnisse mit denen für GCC 4.7.2 und Clang 3.2, so fällt auf, dass GCC in jeder Optimierungsstufe etwa ein Viertel des Clang-Vorsprungs zurückgekratzt hat. Da die Testanwendung in der Zwischenzeit jedoch stark weiterentwickelt wurde, kann man dies nicht sicher auf einen Aufholprozess bei der Codegenerierung von GCC zurückführen. (Dieses Mal habe ich den Anwendungsschnappschuss notiert, aus dem die Zeiteinstellungen abgerufen wurden, und kann ihn wieder verwenden.)

PDATE: GCC 4.8.2 v clang 3.4

Ich beendete das Update für GCC 4.8.1 v Clang 3.3 und sagte, dass ich mich für weitere Updates an denselben Coan-Snaphot halten würde. Aber ich entschied mich stattdessen, diesen Schnappschuss (Rev. 301) und den neuesten Entwicklungsschnappschuss zu testen, der seine Testsuite besteht (Rev. 619). . Dies verleiht den Ergebnissen eine gewisse Länge, und ich hatte ein anderes Motiv:

In meinem ursprünglichen Beitrag habe ich festgestellt, dass ich mich nicht darum bemüht habe, coan auf Geschwindigkeit zu optimieren. Dies war ab rev noch der Fall. 301. Nachdem ich jedoch das Zeitmessgerät in das Coan-Testgeschirr eingebaut hatte, starrte mich jedes Mal, wenn ich die Testsuite durchlief, die Auswirkung der neuesten Änderungen auf die Leistung an. Ich sah, dass es oft überraschend groß war und dass der Trend viel negativer war, als ich es für sinnvoll hielt, an Funktionalität zu gewinnen.

Von rev. 308 hat sich die durchschnittliche Verarbeitungszeit pro Eingabedatei in der Testsuite seit dem ersten Posting hier mehr als verdoppelt. Zu diesem Zeitpunkt drehte ich meine 10-jährige Politik um, mich nicht um die Leistung zu kümmern. In der intensiven Zeit der Überarbeitungen bis zu 619 war die Leistung immer ein Thema, und eine große Anzahl von ihnen wandte sich ausschließlich dem Umschreiben von Schlüssellastträgern in wesentlich schnelleren Zeilen zu (obwohl keine nicht standardmäßigen Compilerfunktionen verwendet wurden). Es wäre interessant zu sehen, wie jeder Compiler auf diese Kehrtwende reagiert.

Hier ist die nun vertraute Timing-Matrix für die letzten beiden Compiler-Builds von 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|

Die Geschichte hier ist nur unwesentlich von GCC-4.8.1 und Clang-3.3 geändert. Der Auftritt von GCC ist ein bisschen besser. Clangs ist eine Kleinigkeit schlimmer. Lärm könnte das erklären. Clang liegt immer noch mit -O2 und -O3 Rändern vorne, die in den meisten Anwendungen keine Rolle spielen, aber für einige von ihnen von Bedeutung sind.

Und hier ist die Matrix für rev. 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|

Nimmt man die Figuren 301 und 619 nebeneinander, so sprechen mehrere Punkte für sich.

  • Ich wollte schnelleren Code schreiben, und beide Compiler bestätigen nachdrücklich meine Bemühungen. Aber:

  • GCC zahlt diese Bemühungen weitaus großzügiger als Clang. Bei der -O2 -Optimierung ist Clangs 619-Build 46% schneller als sein 301-Build: Bei -O3 beträgt die Verbesserung von Clang 31%. Gut, aber in jeder Optimierungsstufe ist der GCC 619 mehr als doppelt so schnell wie der 301.

  • GCC kehrt Clangs frühere Überlegenheit mehr als um. Und in jeder Optimierungsstufe schlägt GCC Clang jetzt um 17%.

  • Clangs Fähigkeit im 301-Build, durch -O3 -Optimierung mehr Hebelwirkung als GCC zu erzielen, ist im 619-Build weg. Keiner der Compiler profitiert sinnvoll von -O3.

Ich war von dieser Umkehrung des Glücks ausreichend überrascht, dass ich vermutete, ich hätte versehentlich einen trägen Build von Clang 3.4 selbst erstellt (da ich ihn aus dem Quellcode erstellt habe). Also habe ich den 619-Test mit dem Clang 3.3 meiner Distribution wiederholt. Die Ergebnisse waren praktisch die gleichen wie für 3.4.

Was die Reaktion auf die Kehrtwende angeht: Bei den Zahlen hier hat Clang viel besser abgeschnitten als GCC, als ich meinen C++ - Code ohne Hilfe verlassen hatte. Als ich mich entschied zu helfen, leistete GCC einen viel besseren Job als Clang.

Ich verstehe diese Beobachtung nicht als Prinzip, nehme aber die Lektion "Welcher Compiler erzeugt die besseren Binärdateien?" ist eine Frage, die, auch wenn Sie die Testsuite angeben, auf die die Antwort relativ sein soll, immer noch keine klare Angelegenheit ist, nur das Timing der Binärdateien vorzunehmen.

Ist Ihre bessere Binärdatei die schnellste Binärdatei oder diejenige, die kostengünstig erstellten Code am besten ausgleicht? Oder kompensieren Sie am besten teuren Code, bei dem die Wartbarkeit und Wiederverwendung Vorrang vor der Geschwindigkeit hat? Dies hängt von der Art und den relativen Gewichten Ihrer Motive zur Erzeugung der Binärdatei und von den Einschränkungen ab, unter denen Sie dies tun.

Und in jedem Fall sollten Sie, wenn Sie sich sehr für die Erstellung der "besten" Binärdateien interessieren, überprüfen, wie aufeinanderfolgende Iterationen von Compilern Ihre Vorstellung von den "besten" über aufeinanderfolgende Iterationen Ihres Codes liefern.

229
Mike Kinghan

Phoronix hat diesbezüglich einige Benchmarks durchgeführt, es handelt sich jedoch um eine Snapshot-Version von Clang/LLVM aus einigen Monaten zurück. Die Folge war, dass die Dinge mehr oder weniger ein Push waren; Weder GCC noch Clang sind in allen Fällen definitiv besser.

Da Sie den neuesten Clang verwenden würden, ist er möglicherweise weniger relevant. Andererseits wird GCC 4.6 voraussichtlich einige Hauptoptimierungen für Core 2 und i7 haben.

Ich gehe davon aus, dass Clangs schnellere Kompilierungsgeschwindigkeit für Originalentwickler angenehmer sein wird. Wenn Sie den Code dann in die Welt hinausschieben, wird Linux Distribution/BSD/etc. Endbenutzer verwenden GCC für die schnelleren Binärdateien.

48
Nietzche-jou

Die Tatsache, dass Clang Code schneller kompiliert, ist möglicherweise nicht so wichtig wie die Geschwindigkeit der resultierenden Binärdatei. Hier ist jedoch ein Reihe von Benchmarks .

16
mcandre

Es gibt insgesamt nur einen sehr geringen Unterschied zwischen GCC 4.8 und Clang 3.3 in Bezug auf die Geschwindigkeit der resultierenden Binärdatei. In den meisten Fällen verhält sich der von beiden Compilern generierte Code ähnlich. Keiner dieser beiden Compiler dominiert den anderen.

Benchmarks, die auf eine signifikante Leistungslücke zwischen GCC und Clang hinweisen, sind zufällig.

Die Programmleistung wird durch die Wahl des Compilers beeinflusst. Wenn ein Entwickler oder eine Gruppe von Entwicklern ausschließlich GCC verwendet, kann erwartet werden, dass das Programm mit GCC etwas schneller als mit Clang ausgeführt wird und umgekehrt.

Aus Entwicklersicht besteht ein bemerkenswerter Unterschied zwischen GCC 4.8+ und Clang 3.3 darin, dass GCC das -Og Befehlszeilenoption. Diese Option ermöglicht Optimierungen, die das Debuggen nicht beeinträchtigen. So ist es beispielsweise immer möglich, genaue Stack-Traces abzurufen. Das Fehlen dieser Option in clang erschwert es einigen Entwicklern, clang als optimierenden Compiler zu verwenden.

12
user811773

Die einzige Möglichkeit, dies festzustellen, besteht darin, es zu versuchen. FWIW Ich habe einige wirklich gute Verbesserungen bei der Verwendung von Apples LLVM gcc 4.2 im Vergleich zu dem regulären gcc 4.2 (für x86-64-Code mit ziemlich viel SSE) gesehen, aber YMMV für verschiedene Codebasen. Angenommen, Sie arbeiten mit x86/x86-64 und kümmern sich wirklich um die letzten paar Prozent. Dann sollten Sie auch Intels ICC ausprobieren, da dies oftmals gcc schlägt - Sie können eine 30-Tage-Testlizenz von intel.com erhalten und probiere es aus.

9
Paul R

Ein merkwürdiger Unterschied, den ich in GCC 5.2.1 und Clang 3.6.2 festgestellt habe, ist, dass, wenn Sie eine kritische Schleife haben wie:

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

Dann wird gcc beim Kompilieren mit -O3 oder -O2, wickeln Sie die Schleife spekulativ achtmal ab. Clang wird es überhaupt nicht abrollen. Durch Versuch und Irrtum habe ich festgestellt, dass in meinem speziellen Fall mit meinen Programmdaten die richtige Menge an Abrollvorgängen fünf ist, so dass GCC-Über- und Unterschreitung klirrt. Ein Überschießen wirkte sich jedoch nachteiliger auf die Leistung aus, sodass die Leistung von gcc hier deutlich schlechter ausfiel.

Ich habe keine Ahnung , ob der sich abrollende Unterschied ein allgemeiner Trend ist oder nur etwas, das für mein Szenario spezifisch war.

Vor einiger Zeit schrieb ich ein ein paar Müllsammler , um mich mehr über die Leistungsoptimierung in C zu unterrichten. Und die Ergebnisse, die ich erhielt, sind in meinem Kopf genug, um ein wenig für Klirren zu sprechen. Zumal es bei der Garbage Collection hauptsächlich um das Verfolgen und Kopieren von Zeigern geht.

Die Ergebnisse sind (Zahlen in Sekunden):

+---------------------+-----+-----+
|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|
+---------------------+-----+-----+

Dies ist alles reiner C-Code, und ich mache keinen Anspruch auf die Leistung der beiden Compiler beim Kompilieren von C++ - Code.

Auf Ubuntu 15.10, x86.64 und einem AMD Phenom II X6 1090T-Prozessor.

7

Grundsätzlich lautet die Antwort: es kommt darauf an. Es gibt viele Benchmarks, die sich auf verschiedene Arten von Anwendungen konzentrieren.

Mein Benchmark für meine App ist: gcc> icc> clang.

Es gibt seltene E/A-Vorgänge, aber viele CPU-Float- und Datenstrukturvorgänge.

compile flags ist -Wall -g -DNDEBUG -O3.

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

4
kimi