Clang vs GCC - qui produit de meilleurs binaires? [fermé]

238

J'utilise actuellement GCC, mais j'ai découvert Clang récemment et j'envisage de changer. Il existe cependant un facteur décisif - la qualité (vitesse, encombrement de la mémoire, fiabilité) des binaires qu'il produit - s'il gcc -O3peut produire un binaire qui s'exécute 1% plus rapidement ou prend 1% de mémoire en moins, c'est une rupture.

Clang offre de meilleures vitesses de compilation et une empreinte mémoire inférieure à celle de GCC, mais je suis vraiment intéressé par les références / comparaisons des logiciels compilés résultants - pourriez-vous me pointer vers certains ou décrire vos expériences?

SF.
la source
5
Cela semble toujours être une question et des réponses précieuses, et beaucoup sont intéressés.
YasserAsmi
9
@YasserAsmi: Et les deux mesures - empreinte mémoire et vitesse d'exécution - sont loin d'être arbitraires ou sujettes à "opinion". Mais il semble que la maladie de Physics.SE se propage ici et les gens ont commencé à voter pour fermer sans lire les détails du texte de la question ici aussi.
SF.
12
la question demande des repères et des comparaisons, la réponse donne les deux ... pourquoi cette opinion au lieu d'une comparaison factuelle?
oemb1905
2
Je ne vois pas pourquoi cette question a été fermée. Qu'il s'agisse d'opinions ou de faits, nous voulons connaître la réponse, et le marquer comme fermé lui donne une teinte négative, alors qu'il ne devrait pas y en avoir.
Timothy Makobu
2
@TomZych: Si vous voulez connaître ces facteurs, posez différentes questions. Celui-ci est très spécifique et sans ambiguïté - demandant la vitesse d'exécution et l'empreinte mémoire. Vous pouvez être intéressé par d'autres facteurs, bons pour vous, cela ne signifie pas que cette question n'est pas valide, elle ne répond tout simplement pas à vos intérêts personnels. C'est comme si vous étiez un programmeur Java et que vous souhaitez fermer chaque question C # car il ne parle pas de Java.
SF.

Réponses:

239

Voici quelques-unes de mes découvertes à jour, bien qu'étroites, avec GCC 4.7.2 et Clang 3.2 pour C ++.

MISE À JOUR: GCC 4.8.1 v clang 3.3 comparaison ci-dessous.

MISE À JOUR: La comparaison GCC 4.8.2 v clang 3.4 y est annexée.

Je maintiens un outil OSS conçu pour Linux avec GCC et Clang, et avec le compilateur de Microsoft pour Windows. L'outil, coan, est un préprocesseur et un analyseur de fichiers source C / C ++ et de lignes de codage de ceux-ci: son profil de calcul se spécialise dans l'analyse et la gestion de fichiers à descente récursive. La branche développement (à laquelle ces résultats se rapportent) comprend actuellement environ 11K LOC dans environ 90 fichiers. Il est codé, maintenant, en C ++ qui est riche en polymorphisme et en modèles et est encore embourbé dans de nombreux correctifs par son passé pas si lointain dans le C. piraté. La sémantique de Move n'est pas expressément exploitée. Il est monofil. Je n'ai consacré aucun effort sérieux à l'optimiser, alors que «l'architecture» reste si largement ToDo.

J'ai utilisé Clang avant 3.2 uniquement comme compilateur expérimental parce que, malgré sa vitesse de compilation et ses diagnostics supérieurs, sa prise en charge standard C ++ 11 était en retard sur la version GCC contemporaine dans les égards exercés par coan. Avec 3.2, cet écart a été comblé.

Mon harnais de test Linux pour les processus de développement actuels de coan représente environ 70 000 fichiers sources dans un mélange de cas de test d'analyseur à un fichier, de tests de résistance consommant des milliers de fichiers et de tests de scénario consommant <1 000 fichiers. En plus de rapporter les résultats du test, le faisceau accumule et affiche les totaux des fichiers consommés et le temps d'exécution consommé en coan (il passe simplement chaque ligne de commande coan à la timecommande Linux et capture et additionne les nombres rapportés). Les délais sont flattés par le fait que n'importe quel nombre de tests qui prennent 0 temps mesurable totalisera tous jusqu'à 0, mais la contribution de ces tests est négligeable. Les statistiques de chronométrage sont affichées à la fin de make checkceci:

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.

J'ai comparé les performances du faisceau de test entre GCC 4.7.2 et Clang 3.2, toutes choses étant égales par ailleurs, sauf les compilateurs. Depuis Clang 3.2, je n'ai plus besoin de différenciation de préprocesseur entre les voies de code que GCC compilera et les alternatives de Clang. J'ai construit dans la même bibliothèque C ++ (GCC) dans chaque cas et exécuté toutes les comparaisons consécutivement dans la même session de terminal.

Le niveau d'optimisation par défaut pour ma version de version est -O2. J'ai également testé avec succès des versions à -O3. J'ai testé chaque configuration 3 fois consécutivement et fait la moyenne des 3 résultats, avec les résultats suivants. Le nombre dans une cellule de données est le nombre moyen de microsecondes consommées par l'exécutable COAN pour traiter chacun des fichiers d'entrée ~ 70K (lecture, analyse et écriture de sortie et diagnostics).

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

Toute application particulière a très probablement des caractéristiques qui jouent injustement avec les forces ou les faiblesses d'un compilateur. Une analyse comparative rigoureuse utilise diverses applications. Dans cet esprit, les caractéristiques remarquables de ces données sont les suivantes:

  1. -O3 optimisation a été légèrement préjudiciable à GCC
  2. -O3 optimisation a été très bénéfique pour Clang
  3. À l'optimisation -O2, GCC était plus rapide que Clang par juste un moustache
  4. À l'optimisation -O3, Clang était beaucoup plus rapide que GCC.

Une autre comparaison intéressante des deux compilateurs est apparue par accident peu de temps après ces résultats. Coan emploie généreusement des pointeurs intelligents et l'un d'entre eux est fortement exercé dans la gestion des fichiers. Ce type de pointeur intelligent particulier avait été tapé dans les versions précédentes à des fins de différenciation du compilateur, pour être un std::unique_ptr<X>si le compilateur configuré avait un support suffisamment mature pour son utilisation comme ça, et sinon un std::shared_ptr<X>. Le biais à std::unique_ptrétait stupide, car ces pointeurs étaient en fait transférés, mais std::unique_ptrressemblaient à l'option de montage pour le remplacement std::auto_ptrà un moment où les variantes C ++ 11 étaient nouvelles pour moi.

Au cours de builds expérimentaux pour évaluer le besoin continu de Clang 3.2 pour cela et une différenciation similaire, j'ai construit par inadvertance std::shared_ptr<X>quand j'avais l'intention de construire std::unique_ptr<X>, et j'ai été surpris de constater que l'exécutable résultant, avec l'optimisation par défaut -O2, était le plus rapide que je avait vu, atteignant parfois 184 ms. par fichier d'entrée. Avec cette seule modification du code source, les résultats correspondants étaient les suivants;

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

Les points à noter ici sont:

  1. Aucun des deux compilateurs ne bénéficie désormais de l'optimisation -O3.
  2. Clang bat GCC tout aussi important à chaque niveau d'optimisation.
  3. Les performances de GCC ne sont que marginalement affectées par le changement de type de pointeur intelligent.
  4. Les performances de -ang O2 de Clang sont affectées de manière importante par le changement de type de pointeur intelligent.

Avant et après le changement de type de pointeur intelligent, Clang est en mesure de créer un exécutable COAN beaucoup plus rapide à l'optimisation -O3, et il peut créer un exécutable tout aussi rapide à -O2 et -O3 lorsque ce type de pointeur est le meilleur - std::shared_ptr<X>- Pour le boulot.

Une question évidente que je ne suis pas compétent pour commenter est pourquoi Clang devrait être en mesure de trouver une accélération de 25% -O2 dans mon application lorsqu'un type de pointeur intelligent très utilisé est passé d'unique à partagé, alors que GCC est indifférent au même changement. Je ne sais pas non plus si je devrais encourager ou huer la découverte que l'optimisation -O2 de Clang abrite une sensibilité aussi énorme à la sagesse de mes choix de pointeurs intelligents.

MISE À JOUR: GCC 4.8.1 v clang 3.3

Les résultats correspondants sont maintenant:

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

Le fait que les quatre exécutables prennent désormais beaucoup plus de temps en moyenne qu'auparavant pour traiter 1 fichier ne reflète pas les performances des derniers compilateurs. Cela est dû au fait que la branche de développement ultérieure de l'application de test a entre-temps pris beaucoup de sophistication d'analyse et la paie rapidement. Seuls les ratios sont significatifs.

Les points à noter ne sont pas d'une nouveauté saisissante:

  • GCC est indifférent à l'optimisation -O3
  • clang bénéficie très marginalement de l'optimisation -O3
  • clang bat GCC d'une marge tout aussi importante à chaque niveau d'optimisation.

En comparant ces résultats avec ceux de GCC 4.7.2 et clang 3.2, il ressort que GCC a récupéré environ un quart de l'avance de clang à chaque niveau d'optimisation. Mais puisque l'application de test a été fortement développée entre-temps, on ne peut pas l'attribuer en toute confiance à un rattrapage dans la génération de code de GCC. (Cette fois, j'ai noté l'instantané de l'application à partir duquel les timings ont été obtenus et je peux le réutiliser.)

MISE À JOUR: GCC 4.8.2 v clang 3.4

J'ai terminé la mise à jour de GCC 4.8.1 v Clang 3.3 en disant que je m'en tiendrais au même instantané de coan pour d'autres mises à jour. Mais j'ai décidé à la place de tester sur cet instantané (rév. 301) et sur le dernier instantané de développement que j'ai qui réussit sa suite de tests (rév. 619). Cela donne aux résultats un peu de longitude, et j'avais un autre motif:

Mon message d'origine indiquait que je n'avais consacré aucun effort à optimiser le rouage pour la vitesse. C'était encore le cas au rév. 301. Cependant, après avoir intégré l'appareil de chronométrage dans le harnais de test coan, chaque fois que je courais la suite de tests, l'impact sur les performances des derniers changements me regardait en face. J'ai vu qu'elle était souvent étonnamment grande et que la tendance était plus fortement négative que je ne le pensais méritée par des gains de fonctionnalité.

Par rev. 308 le temps de traitement moyen par fichier d'entrée dans la suite de tests avait bien plus que doublé depuis la première publication ici. À ce stade, j'ai fait volte-face sur ma politique de 10 ans de ne pas se soucier de la performance. Dans la vague intensive de révisions, jusqu'à 619, les performances ont toujours été prises en compte et un grand nombre d'entre elles sont allées uniquement à la réécriture de supports de charge clés sur des lignes fondamentalement plus rapides (mais sans utiliser de fonctionnalités de compilateur non standard pour le faire). Il serait intéressant de voir la réaction de chaque compilateur à ce demi-tour,

Voici la matrice de synchronisation désormais familière pour les deux dernières compilations de la version 301 des compilateurs:

coan - rev.301 résultats

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

L'histoire ici n'est que légèrement modifiée par rapport à GCC-4.8.1 et Clang-3.3. La performance de GCC est un peu meilleure. Clang est un peu pire. Le bruit pourrait bien expliquer cela. Clang sort toujours en tête -O2et des -O3marges qui n'auraient pas d'importance dans la plupart des applications mais qui seraient importantes pour quelques-unes.

Et voici la matrice pour rev. 619.

coan - rev.619 résultats

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

Prenant côte à côte les figures 301 et 619, plusieurs points se manifestent.

  • Je visais à écrire du code plus rapidement, et les deux compilateurs justifient avec force mes efforts. Mais:

  • Le CCG rembourse ces efforts beaucoup plus généreusement que Clang. À l' -O2 optimisation, la version 619 de Clang est 46% plus rapide que sa version 301: à -O3Clang, l'amélioration est de 31%. Bien, mais à chaque niveau d'optimisation, la version 619 de GCC est plus de deux fois plus rapide que sa 301.

  • GCC fait plus qu'annuler l'ancienne supériorité de Clang. Et à chaque niveau d'optimisation, GCC bat désormais Clang de 17%.

  • La capacité de Clang dans la version 301 à obtenir plus de levier que GCC grâce à l' -O3optimisation a disparu dans la version 619. Aucun des compilateurs ne gagne de manière significative -O3.

J'ai été suffisamment surpris par ce renversement de fortune pour que je soupçonne que j'ai pu accidentellement créer une version lente de clang 3.4 lui-même (puisque je l'ai construit à partir de la source). J'ai donc relancé le test 619 avec le stock de ma distribution Clang 3.3. Les résultats étaient pratiquement les mêmes que pour 3.4.

Donc, en ce qui concerne la réaction au demi-tour: Sur les chiffres ici, Clang a fait beaucoup mieux que GCC à la vitesse d'essorage de mon code C ++ quand je ne lui donnais aucune aide. Quand j'ai décidé d'aider, GCC a fait un bien meilleur travail que Clang.

Je n'élève pas cette observation en principe, mais je prends la leçon que "Quel compilateur produit les meilleurs binaires?" est une question qui, même si vous spécifiez la suite de tests à laquelle la réponse doit être relative, n'est toujours pas une question claire de simplement chronométrer les binaires.

Votre meilleur binaire est-il le binaire le plus rapide, ou est-ce celui qui compense le mieux le code conçu à moindre coût? Ou compense-t-il mieux le code élaboré de manière coûteuse qui donne la priorité à la maintenabilité et à la réutilisation par rapport à la vitesse? Cela dépend de la nature et des poids relatifs de vos motifs de production du binaire et des contraintes sous lesquelles vous le faites.

Et dans tous les cas, si vous vous souciez profondément de créer "les meilleurs" binaires, vous feriez mieux de continuer à vérifier comment les itérations successives des compilateurs donnent suite à votre idée du "meilleur" par rapport aux itérations successives de votre code.

Mike Kinghan
la source
9
pourquoi est-ce plus rapide? par exemple, le compilateur Intel a utilisé des spécialités de puces Intel. qu'est-ce que clang utilise pour obtenir un avantage? le code peut-il être réécrit pour que gcc ait les mêmes performances?
kirill_igum
27
@krill_igum GCC et clang sont des programmes différents (extrêmement complexes) écrits par différents groupes de programmeurs pour faire le même travail: traduire le code source en code objet. Il est presque inévitable que l'un d'entre eux fasse ce travail de manière mesurable mieux que l'autre dans n'importe quel test choisi à tout moment. Il n'est pas nécessaire qu'il y ait une «chose» spéciale que le gagnant «utilise» pour «obtenir un avantage», et puisque les deux programmes sont open source, ils n'ont aucun secret l'un pour l'autre.
Mike Kinghan
3
Il est possible d'utiliser kcachegrindpour identifier les fonctions où les exécutables générés diffèrent en termes de performances.
4
-1: Il s'agit plus d'un roman (ou d'un article de blog) que d'une réponse.
John Saunders
60
@JohnSaunders: Ce qui est pour une personne une réponse détaillée et approfondie, pour un autre est un roman indigne de leur attention. Dites-moi ce qui distingue ces deux personnes.
SF.
48

Phoronix a fait quelques benchmarks à ce sujet, mais il s'agit d'une version instantanée de Clang / LLVM datant de quelques mois. Les résultats étant que les choses étaient plus ou moins une poussée; ni GCC ni Clang ne sont définitivement meilleurs dans tous les cas.

Puisque vous utiliseriez le dernier Clang, c'est peut-être un peu moins pertinent. Là encore, GCC 4.6 devrait avoir quelques optimisations majeures pour Core 2 et i7, apparemment.

Je pense que la vitesse de compilation plus rapide de Clang sera plus agréable pour les développeurs d'origine, puis lorsque vous diffuserez le code dans le monde, Linux distro / BSD / etc. les utilisateurs finaux utiliseront GCC pour les binaires les plus rapides.

Nietzche-jou
la source
2
Aujourd'hui encore, je lance quelques tests de référence sur la vitesse de compilation de Clang et c'est très décevant pour le C. pur. La compilation de fichiers 35 C avec 270 KLOC clang n'était que 25% plus rapide. Quand je vois à quelle vitesse tinycc est sur Linux, c'est un mauvais résultat pour un nouveau compilateur écrit. Cela s'améliore lorsque vous utilisez les optimisations -O2 / -O3, mais comme elles sont utilisées pour la version, les performances du compilateur n'ont pas d'importance dans ce cas.
Lothar
7
@mcandre Peut-être que Nietzche-jou a été compilé avec Clang, alors que vous étiez compilé avec GCC.
Mateen Ulhaq
18

Le fait que Clang compile du code plus rapidement peut ne pas être aussi important que la vitesse du binaire résultant. Cependant, voici une série de repères .

mcandre
la source
12
En fait, c'est le cas. Pendant le développement, le temps de compilation (et la consommation de ressources due à la compilation) sont beaucoup plus un goulot d'étranglement que les performances binaires. Après tout, nous compilons en mode débogage à ce stade. Ce n'est que lorsque vient l'étape de tester et d'expédier que vous passez en mode Release et essayez d'obtenir le plus rapidement possible un binaire.
Matthieu M.
3
@ Matthieu M: Je jure que cette réponse disait "peut ..", comme s'il évoquait un problème potentiel. Je suppose que cela valait peut-être la peine d'être mentionné parce que c'était, tu sais, lié au PO.
JM Becker
D'accord, bien que tous les bons points ici. Je préfère ajouter un 2ème ou 3ème disque RAID 0, un SSD ou plus de RAM et plus rapide et obtenir les meilleures performances .exe - à condition que ces mesures puissent vous amener à la parité ou à la fermeture. Il est également parfois utile de développer avec plusieurs compilateurs. Cela peut vous faire prendre conscience des fonctionnalités non portables ET détecter des erreurs qui autrement ne sont pas détectées, ou conduire à des jours de temps perdu à essayer de déboguer du code qu'un meilleur compilateur aurait averti / commis une erreur.
J'ai essayé aujourd'hui de comparer certains codes entiers critiques de performances serrés que j'ai écrits et GCC a fonctionné beaucoup plus rapidement (22S clang-llvm 25S) en utilisant à la fois -O2 et -O3. Pensez à utiliser les commutateurs du compilateur (gcc ou clang) pour couvrir la plupart des fonctionnalités non standard et des avertissements statiques. Dans votre propre grand projet, pas en compilant par lots le code d'autres ppl, vous faites quelque chose de mal dans votre système de build si le temps de compilation domine le temps de liaison. Il existe des outils comme ccache.samba.org qui vous aident si vous nettoyez souvent. Un autre problème lié au changement de compilateur est l'investissement constant dans les tests / la validation qui est jeté.
Rob11311
code.google.com/p/distcc est un autre projet qui peut accélérer les temps de compilation en bloc, si une bibliothèque entière a besoin d'être recompilée en raison de modifications de la structure des données ou à des fins de vérification / validation
Rob11311
11

Il y a très peu de différence globale entre GCC 4.8 et clang 3.3 en termes de vitesse du binaire résultant. Dans la plupart des cas, le code généré par les deux compilateurs fonctionne de manière similaire. Aucun de ces deux compilateurs ne domine l'autre.

Les repères indiquant qu'il existe un écart de performance significatif entre GCC et clang sont fortuits.

Les performances du programme sont affectées par le choix du compilateur. Si un développeur ou un groupe de développeurs utilise exclusivement GCC, le programme peut s'exécuter légèrement plus rapidement avec GCC qu'avec clang, et vice versa.

Du point de vue du développeur, une différence notable entre GCC 4.8+ et clang 3.3 est que GCC a l' -Ogoption de ligne de commande. Cette option permet des optimisations qui n'interfèrent pas avec le débogage, ainsi, par exemple, il est toujours possible d'obtenir des traces de pile précises. L'absence de cette option dans clang rend clang plus difficile à utiliser comme compilateur d'optimisation pour certains développeurs.


la source
Dernièrement, (3.3 et 4.8), je ne vois même pas beaucoup de différence entre le temps de compilation. (dans "mes" programmes avec des temps de compilation entre 10secondes et 30 secondes).
alfC
9

La seule façon de le déterminer est de l'essayer. FWIW J'ai vu de très bonnes améliorations en utilisant LLVM gcc 4.2 d'Apple par rapport au gcc 4.2 normal (pour le code x86-64 avec beaucoup de SSE), mais YMMV pour différentes bases de code. En supposant que vous travaillez avec x86 / x86-64 et que vous vous souciez vraiment des derniers pour cent, vous devez également essayer l'ICC d'Intel, car cela peut souvent battre gcc - vous pouvez obtenir une licence d'évaluation de 30 jours sur intel.com et essayez-le.

Paul R
la source
8

Une différence particulière que j'ai notée sur gcc 5.2.1 et clang 3.6.2 est que si vous avez une boucle critique comme:

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

Ensuite, gcc, lors de la compilation avec -O3ou -O2, déroulera la boucle de façon spéculative huit fois. Clang ne le déroulera pas du tout. Par essais et erreurs, j'ai trouvé que dans mon cas spécifique avec les données de mon programme, la bonne quantité de déroulement est de cinq dépassements et de clics inférieurs. Cependant, le dépassement a été plus préjudiciable à la performance, donc gcc s'est bien comporté ici.

Je n'ai aucune idée si la différence de déroulement est une tendance générale ou juste quelque chose qui était spécifique à mon scénario.

Il y a quelque temps, j'ai écrit quelques ramasse-miettes pour m'apprendre davantage sur l'optimisation des performances en C. Et les résultats que j'ai obtenus sont dans mon esprit assez pour favoriser légèrement le clang. D'autant plus que la collecte des ordures concerne principalement la recherche de pointeurs et la copie de mémoire.

Les résultats sont (nombres en secondes):

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

Il s'agit uniquement de code C pur et je ne prétends pas aux performances des compilateurs lors de la compilation de code C ++.

Sur Ubuntu 15.10, x86.64 et un processeur AMD Phenom (tm) II X6 1090T.

Björn Lindqvist
la source
4

Fondamentalement, la réponse est: cela dépend. Il existe de nombreux repères se concentrant sur différents types d'applications.

Mon repère sur mon application est: gcc> icc> clang.

Il existe des E / S rares, mais de nombreuses opérations de flottement de CPU et de structure de données.

les drapeaux de compilation sont -Wall -g -DNDEBUG -O3.

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

kimi
la source