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 -O3
peut 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?
Réponses:
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
time
commande 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 demake check
ceci: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).
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:
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 unstd::shared_ptr<X>
. Le biais àstd::unique_ptr
était stupide, car ces pointeurs étaient en fait transférés, maisstd::unique_ptr
ressemblaient à l'option de montage pour le remplacementstd::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 construirestd::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;Les points à noter ici sont:
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:
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:
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
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
-O2
et des-O3
marges 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
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: à-O3
Clang, 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'
-O3
optimisation 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.
la source
kcachegrind
pour identifier les fonctions où les exécutables générés diffèrent en termes de performances.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.
la source
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 .
la source
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'
-Og
option 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
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.
la source
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:
Ensuite, gcc, lors de la compilation avec
-O3
ou-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):
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.
la source
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
la source