Hier sind einige aktuelle, wenn auch enge Ergebnisse von mir mit GCC 4.7.2 und Clang 3.2 für C ++.
UPDATE: GCC 4.8.1 v Clang 3.3 Vergleich unten angefügt.
UPDATE: GCC 4.8.2 v Clang 3.4 Vergleich ist dem beigefügt.
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 für solche: seine Hauptprofile für das rechnerische Abstiegs-Parsing und die Dateiverwaltung. Der Entwicklungszweig (auf den sich diese Ergebnisse beziehen) umfasst derzeit etwa 11.000 LOC in etwa 90 Dateien. Es ist jetzt in C ++ codiert, das reich an Polymorphismus und Vorlagen ist und dennoch in vielen Patches durch seine nicht allzu ferne Vergangenheit in zusammengehacktem C verstrickt ist. 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 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 ausgeübten Hinsicht hinterherhinkt. Mit 3.2 wurde diese Lücke geschlossen.
Mein Linux-Testkabel für aktuelle Coan-Entwicklungsprozesse verarbeitet ungefähr 70.000 Quelldateien in einer Mischung aus Parser-Testfällen mit einer Datei, Stresstests, die Tausende von Dateien verbrauchen, und Szenariotests, die weniger als 1 KB Dateien verbrauchen. Das Harness meldet nicht nur die Testergebnisse, sondern sammelt auch die Gesamtzahl der verbrauchten Dateien und die in coan verbrauchte Laufzeit (es übergibt einfach jede coan-Befehlszeile an den Linux- time
Befehl und erfasst und addiert die gemeldeten Zahlen). Das Timing wird durch die Tatsache geschmeichelt, dass eine beliebige Anzahl von Tests, die 0 messbare Zeit benötigen, alle 0 ergeben, aber der Beitrag solcher Tests ist vernachlässigbar. Die Timing-Statistiken werden am Ende folgendermaßen angezeigt 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.
Ich habe die Leistung des Testkabels zwischen GCC 4.7.2 und Clang 3.2 verglichen, wobei alle Dinge außer den Compilern gleich waren. Ab Clang 3.2 benötige ich keine Präprozessor-Differenzierung mehr zwischen Code-Traktaten, die GCC kompiliert, und Clang-Alternativen. Ich habe jeweils dieselbe C ++ - Bibliothek (GCC) 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 die drei Ergebnisse mit den folgenden Ergebnissen gemittelt. Die Zahl in einer Datenzelle ist die durchschnittliche Anzahl von Mikrosekunden, die von der ausführbaren Coan-Datei benötigt werden, um jede der ~ 70K-Eingabedateien zu verarbeiten (Lese-, Analyse- und Schreibausgabe 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 Eigenschaften aufweist, die den Stärken oder Schwächen eines Compilers nicht gerecht werden. Rigoroses Benchmarking verwendet verschiedene Anwendungen. Vor diesem Hintergrund sind die bemerkenswerten Merkmale dieser Daten:
- -O3-Optimierung war für GCC geringfügig schädlich
- -O3-Optimierung war für Clang von großem Vorteil
- Bei der -O2-Optimierung war GCC nur um einen Whisker schneller als Clang
- Bei der -O3-Optimierung war Clang wesentlich schneller als GCC.
Ein weiterer interessanter Vergleich der beiden Compiler ergab sich zufällig kurz nach diesen Erkenntnissen. Coan verwendet großzügig intelligente Zeiger, und einer davon wird in der Dateiverwaltung stark ausgeübt. Dieser spezielle Smart-Pointer-Typ wurde in früheren Releases aus Gründen der Compiler-Differenzierung typisiert, um zu sein, std::unique_ptr<X>
ob der konfigurierte Compiler eine ausreichend ausgereifte Unterstützung für seine Verwendung als solche hatte, und ansonsten eine std::shared_ptr<X>
. Die Tendenz zu std::unique_ptr
war dumm, da diese Zeiger tatsächlich herum übertragen wurden, aber std::unique_ptr
wie die passende Option zum Ersetzen std::auto_ptr
zu einem Zeitpunkt aussahen,
als die C ++ 11-Varianten für mich neu waren.
Während experimenteller Builds, um das anhaltende Bedürfnis von Clang 3.2 nach dieser und einer ähnlichen Differenzierung zu messen, habe ich versehentlich gebaut,
std::shared_ptr<X>
als ich beabsichtigt hatte zu bauen std::unique_ptr<X>
, und war überrascht zu beobachten, dass die resultierende ausführbare Datei mit der Standard-O2-Optimierung die schnellste I war hatte gesehen, manchmal 184 ms zu erreichen. pro Eingabedatei. Bei dieser einen Änderung des Quellcodes waren dies die entsprechenden Ergebnisse.
| -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 wichtigsten Punkte hier sind:
- Keiner der Compiler profitiert jetzt überhaupt von der -O3-Optimierung.
- Clang schlägt GCC auf jeder Optimierungsebene genauso wichtig.
- Die Leistung von GCC wird durch die Änderung des Smart-Pointer-Typs nur geringfügig beeinflusst.
- Die -O2-Leistung von Clang wird maßgeblich durch die Änderung des Smart-Pointer-Typs beeinflusst.
Vor und nach der Änderung des Smart-Pointer-Typs kann Clang bei -O3-Optimierung eine wesentlich schnellere ausführbare Coan-Datei erstellen und bei -O2 und -O3 eine ebenso schnellere ausführbare Datei erstellen, wenn dieser Zeigertyp der beste ist - std::shared_ptr<X>
- für die Arbeit.
Eine offensichtliche Frage, die ich nicht kommentieren kann, ist, warum
Clang in meiner Anwendung eine Beschleunigung von 25% -O2 finden sollte, wenn ein häufig verwendeter Smart-Pointer-Typ von eindeutig zu gemeinsam geändert wird, während GCC gleichgültig ist zur gleichen Änderung. 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, bejubeln oder aushöhlen sollte.
UPDATE: GCC 4.8.1 v clang 3.3
Die entsprechenden Ergebnisse sind jetzt:
| -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 jetzt eine viel längere durchschnittliche Zeit als zuvor benötigen, um eine Datei zu verarbeiten, spiegelt nicht die Leistung der neuesten Compiler wider. Dies liegt an der Tatsache, dass der spätere Entwicklungszweig der Testanwendung in der Zwischenzeit viel Analyse-Raffinesse angenommen hat und sich schnell bezahlt macht. Nur die Verhältnisse sind signifikant.
Die Punkte der Bemerkung sind jetzt nicht auffallend neu:
- GCC ist der -O3-Optimierung gleichgültig
- Clang profitiert nur sehr geringfügig von der -O3-Optimierung
- Clang schlägt GCC auf jeder Optimierungsstufe um einen ähnlich wichtigen Vorsprung.
Vergleicht man diese Ergebnisse mit denen für GCC 4.7.2 und Clang 3.2, so fällt auf, dass GCC auf jeder Optimierungsstufe etwa ein Viertel des Clang-Vorsprungs zurückgefordert hat. Da die Testanwendung inzwischen stark entwickelt 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 Timings abgerufen wurden, und kann ihn wieder verwenden.)
UPDATE: GCC 4.8.2 v clang 3.4
Ich habe das Update für GCC 4.8.1 v Clang 3.3 abgeschlossen und gesagt, dass ich mich für weitere Updates an dasselbe Coan-Snaphot halten würde. Aber ich habe mich stattdessen entschlossen, diesen Snapshot (Rev. 301) und den neuesten Entwicklungs-Snapshot zu testen, der die Testsuite (Rev. 619) besteht. Dies gibt den Ergebnissen ein wenig Länge, und ich hatte ein anderes Motiv:
In meinem ursprünglichen Beitrag wurde festgestellt, dass ich keine Anstrengungen unternommen hatte, um den Coan auf Geschwindigkeit zu optimieren. Dies war ab rev. 301. Nachdem ich jedoch den Zeitmessapparat in den Coan-Testgurt eingebaut hatte, starrten mich die Auswirkungen der letzten Änderungen auf die Leistung jedes Mal an, wenn ich die Testsuite durchführte. Ich sah, dass es oft überraschend groß war und dass der Trend stärker negativ war, als ich es von Funktionsgewinnen verdient hatte.
Durch rev. 308 Die durchschnittliche Verarbeitungszeit pro Eingabedatei in der Testsuite hatte sich seit der ersten Veröffentlichung 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 Flut von Revisionen war die Leistung von bis zu 619 immer eine Überlegung, und eine große Anzahl von ihnen ging lediglich dazu über, wichtige Lastträger auf grundlegend schnelleren Zeilen neu zu schreiben (allerdings ohne die Verwendung von nicht standardmäßigen Compilerfunktionen). Es wäre interessant zu sehen, wie jeder Compiler auf diese Kehrtwende reagiert.
Hier ist die jetzt bekannte Zeitmatrix für die letzten beiden Compiler-Builds von Rev. 301:
coan - rev.301 Ergebnisse
| -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 von GCC-4.8.1 und Clang-3.3 nur unwesentlich geändert. GCCs Auftritt ist eine Kleinigkeit besser. Clang's ist eine Kleinigkeit schlimmer. Lärm könnte das gut erklären. Clang hat immer noch einen Vorsprung -O2
und -O3
Margen, die in den meisten Anwendungen keine Rolle spielen würden, aber für einige von Bedeutung wären.
Und hier ist die Matrix für rev. 619.
coan - rev.619 Ergebnisse
| -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|
Wenn man die Figuren 301 und 619 nebeneinander betrachtet, sprechen mehrere Punkte aus.
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 zurück als Clang. Bei der -O2
Optimierung ist Clangs 619-Build 46% schneller als sein 301-Build: Bei -O3
Clangs Verbesserung 31%. Gut, aber auf jeder Optimierungsstufe ist der 619-Build von GCC mehr als doppelt so schnell wie der 301.
GCC kehrt Clangs frühere Überlegenheit mehr als um. Und auf jeder Optimierungsstufe schlägt GCC Clang jetzt um 17%.
Clangs Fähigkeit im 301-Build, durch -O3
Optimierung mehr Hebelkraft als GCC zu erzielen, ist im 619-Build nicht mehr vorhanden. Keiner der Compiler profitiert sinnvoll von -O3
.
Ich war von dieser Umkehrung des Glücks ausreichend überrascht, dass ich vermutete, dass ich versehentlich einen trägen Build von Clang 3.4 selbst erstellt habe (da ich ihn aus dem Quellcode erstellt habe). Also habe ich den 619-Test mit Clang 3.3 meiner Distribution wiederholt. Die Ergebnisse waren praktisch die gleichen wie für 3.4.
Was die Reaktion auf die Kehrtwende betrifft: Bei den Zahlen hier hat Clang viel besser als GCC abgeschnitten, als ich ihm keine Hilfe gegeben habe. Als ich mich entschied zu helfen, hat GCC einen viel besseren Job gemacht als Clang.
Ich erhebe diese Beobachtung nicht zu einem Prinzip, aber ich nehme die Lektion: "Welcher Compiler erzeugt die besseren Binärdateien?" ist eine Frage, bei der es, selbst wenn Sie die Testsuite angeben, zu der die Antwort relativ sein soll, nicht eindeutig darum geht, nur die Binärdateien zeitlich zu steuern.
Ist Ihre bessere Binärdatei die schnellste Binärdatei oder diejenige, die billig gestalteten Code am besten kompensiert? Oder kompensieren Sie am besten teuer
gestalteten Code, bei dem Wartbarkeit und Wiederverwendung Vorrang vor Geschwindigkeit haben? Dies hängt von der Art und dem relativen Gewicht Ihrer Motive für die Erstellung der Binärdatei sowie von den Einschränkungen ab, unter denen Sie dies tun.
Wenn Sie sich auf jeden Fall für das Erstellen der "besten" Binärdateien interessieren, sollten Sie immer wieder überprüfen, wie aufeinanderfolgende Iterationen von Compilern Ihre Vorstellung von "den besten" über aufeinanderfolgende Iterationen Ihres Codes hinweg liefern.