web-dev-qa-db-fra.com

Clang vs GCC - qui produit de meilleurs fichiers binaires?

J'utilise actuellement GCC, mais j'ai découvert Clang récemment et je réfléchis à changer de poste. Cependant, il reste un facteur décisif: la qualité (vitesse, encombrement de la mémoire, fiabilité) des fichiers binaires qu’elle produit: si gcc -O3 peut produire un fichier binaire fonctionnant 1% plus vite ou utilisant 1% de mémoire en moins, c’est un deal-breaker.

Clang bénéficie d'une vitesse de compilation supérieure et d'une empreinte mémoire inférieure à celle de GCC, mais je suis vraiment intéressé par les points de repère/comparaisons des logiciels compilés ainsi obtenus. Pouvez-vous m'en indiquer certaines ou décrire vos expériences?

224
SF.

Voici quelques découvertes à jour, quoique étroites, de GCC 4.7.2 et Clang 3.2 pour C++.

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

MISE À JOUR: La comparaison de GCC 4.8.2 v Clang 3.4 est ajoutée à celle.

Je gère un outil OSS conçu pour Linux avec GCC et Clang, ainsi qu'avec le compilateur Microsoft pour Windows. L'outil, coan, est un préprocesseur et un analyseur de fichiers source C/C++ et de lignes de code de tels: son profil de calcul majeur sur l'analyse et la gestion de fichiers à descente récursive. La branche de développement (à laquelle ces résultats se rapportent) comprend actuellement environ 11K LOC dans environ 90 dossiers. Il est codé, à présent, en C++, riche en polymorphisme et en modèles, mais est toujours embourbé dans de nombreux patchs par son passé pas si lointain en C haché ensemble. Les sémantiques de déplacement ne sont pas explicitement exploitées. Il est simple thread. Je n’ai pas fait d’efforts sérieux pour l’optimiser, alors que "l’architecture" reste en grande partie à faire.

J’ai utilisé Clang avant 3.2 uniquement à titre de compilateur expérimental car, malgré sa vitesse de compilation et ses diagnostics supérieurs, sa prise en charge standard C++ 11 était à la traîne par rapport à la version actuelle de GCC par rapport à coan. Avec 3.2, cet écart a été comblé.

Mon système de test Linux pour le développement actuel de coan traite environ 70K fichiers sources dans une combinaison de tests d’analyseur à un fichier, de tests de tension utilisant des milliers de fichiers et de tests de scénarios utilisant moins de fichiers de 1K. En plus de rapporter les résultats du test, le harnais accumule et affiche le total des fichiers consommés et le temps d’exécution consommé dans coan (il passe simplement chaque ligne de commande coan à la commande Linux time et enregistre et additionne les nombres rapportés. ) Les temps sont flattés par le fait que tout nombre d'essais prenant 0 temps mesurable totalisera tous 0, mais la contribution de tels essais est négligeable. Les statistiques de chronométrage sont affichées à la fin de make check comme ceci:

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érencier les préprocesseurs entre les codes que GCC va compiler et les alternatives Clang. J'ai construit la même bibliothèque C++ (GCC) dans chaque cas et ai 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 publication est -O2. J'ai également testé avec succès les versions de -O3. J'ai testé chaque configuration 3 fois de suite et en moyenne les 3 résultats, avec les résultats suivants. Le nombre dans une cellule de données est le nombre moyen de microsecondes consommés par l'exécutable coan pour traiter chacun des fichiers d'entrée d'environ 70 000 (sortie, lecture et diagnostic).

          | -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 est très susceptible d'avoir des traits qui jouent injustement sur les forces ou les faiblesses d'un compilateur. Un benchmarking rigoureux utilise diverses applications. Dans cet esprit, les caractéristiques remarquables de ces données sont les suivantes:

  1. -O3 optimisation était légèrement préjudiciable à GCC
  2. -O3 optimisation était particulièrement bénéfique pour Clang
  3. À l’optimisation à -O2, GCC était plus rapide que Clang
  4. À l’optimisation à -03, Clang était nettement plus rapide que GCC.

Une autre comparaison intéressante des deux compilateurs est apparue par accident peu après ces conclusions. Coan utilise 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é typé 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 développé pour son utilisation, et sinon un std::shared_ptr<X>. Le biais envers std::unique_ptr était insensé, car ces pointeurs étaient en fait transférés, mais std::unique_ptr ressemblait à l'option la plus appropriée pour remplacer std::auto_ptr à un moment où les variantes de C++ 11 étaient nouvelles. tome.

Au cours de constructions expérimentales visant à évaluer le besoin continu de Clang 3.2 pour cette différenciation similaire, j’ai construit par inadvertance std::shared_ptr<X> alors que 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 j'avais vu, atteignant parfois 184 ms. par fichier d'entrée. Avec ce seul changement au code source, les résultats correspondants étaient ceux-ci;

          | -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 compilateur 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 Clang sur 02 sont fortement affectées 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 de type coan beaucoup plus rapide avec une optimisation de -O3, et un exécutable tout aussi rapide avec -02 et -O3 lorsque ce type de pointeur est le meilleur - std::shared_ptr<X> - pour le travail.

Une question évidente sur laquelle je ne suis pas compétent pour commenter est pourquoi Clang devrait pouvoir trouver une accélération de 25% -O2 dans mon application lorsqu'un le type de pointeur intelligent très utilisé est remplacé par unique, alors que GCC est indifférent au même changement. Je ne sais pas non plus si je devrais me réjouir de la découverte selon laquelle l'optimisation de Clang's -O2 recèle une telle sensibilité à la sagesse de mes choix de pointeurs intelligents.

MISE À JOUR: GCC 4.8.1 v Clang 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 maintenant un temps moyen beaucoup plus long qu'auparavant pour traiter un fichier ne ( ne reflète pas les performances des derniers compilateurs. Cela est dû au fait que la dernière branche de développement de l’application de test a pris beaucoup de sophistication analytique entre-temps et la paye rapidement. Seuls les ratios sont significatifs.

Les points à noter maintenant ne sont pas particulièrement nouveaux:

  • GCC est indifférent à l'optimisation -O3
  • clang ne profite que très peu de l'optimisation -O3
  • clang bat GCC avec une marge tout aussi importante à chaque niveau d’optimisation.

Si l'on compare ces résultats à ceux de GCC 4.7.2 et de Clang 3.2, il ressort que GCC a récupéré environ un quart de l'avance de clang à chaque niveau d'optimisation. Mais depuis que l'application test a été fortement développée, on ne peut attribuer cela avec confiance à un rattrapage dans la génération de code de GCC. (Cette fois, j'ai noté l'instantané d'application avec lequel les minutages ont été obtenus et je peux l'utiliser à nouveau.)

MISE À JOUR: GCC 4.8.2 v Clang 3.4

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

Mon message initial indiquait que je n'avais consacré aucun effort à l'optimisation de la vitesse de rotation. C'était toujours le cas dès le début. 301. Cependant, après avoir intégré l'appareil de chronométrage dans le harnais de test du coan, chaque fois que j'ai exécuté la suite de tests, l'impact sur la performance des derniers changements m'observait. J'ai vu qu'il était souvent étonnamment grand et que la tendance était plus fortement négative que ce que je pensais être mérité 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 le premier affichage ici. À ce stade, j’ai fait volte-face avec ma politique de 10 ans de ne pas se préoccuper de la performance. Dans la série intensive de révisions, jusqu'à 619 performances ont toujours été prises en compte et un grand nombre d'entre elles se sont contentées de réécrire les porteurs clés sur des lignes fondamentalement plus rapides (sans toutefois utiliser de fonctionnalités de compilateur non standard à cette fin). Il serait intéressant de voir la réaction de chaque compilateur à ce demi-tour,

Voici la matrice de timings maintenant familière pour les versions les plus récentes des deux compilateurs de rev.301:

( 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 marginalement modifiée par rapport à GCC-4.8.1 et Clang-3.3. Les résultats de GCC sont un peu meilleurs. Clang est un peu pire. Le bruit pourrait bien expliquer cela. Clang arrive toujours en avance par -O2 et -O3 marges qui importent peu dans la plupart des applications, mais qui importent beaucoup.

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|

Si on prend côte à côte les figures 301 et 619, plusieurs points se dégagent.

  • Je souhaitais écrire du code plus rapide, et les deux compilateurs revendiquent mes efforts avec détermination. Mais:

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

  • GCC renverse plus que l'ancienne supériorité de Clang. Et à chaque niveau d'optimisation, GCC bat maintenant Clang de 17%.

  • La capacité de Clang dans la version 301 à obtenir plus d’exploitation que GCC grâce à l’optimisation de -O3 a disparu de la version 619. Ni l'un ni l'autre compilateur ne tire profit de -O3.

J'ai été suffisamment surpris par ce renversement de situation pour me douter que j'aurais pu créer accidentellement une construction lente de Clang 3.4 (depuis que je l'ai construite à 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: en ce qui concerne les chiffres, Clang a fait beaucoup mieux que GCC à la vitesse de sortie de mon code C++ alors que je ne lui fournissais aucune aide. Lorsque 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 tire la leçon suivante: "Quel compilateur produit les meilleurs fichiers binaires?" C’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 précise de simplement synchroniser les binaires.

Votre meilleur fichier binaire est-il le plus rapide, ou est-ce celui qui compense le mieux un code fabriqué à moindre coût? Ou compense-t-il le mieux le code coûteux qui donne la priorité à la maintenabilité et à la réutilisation plutôt qu'à la vitesse? Cela dépend de la nature et du poids relatif de vos motivations pour produire le binaire et des contraintes sous lesquelles vous le faites.

Et dans tous les cas, si vous êtes soucieux de créer les "meilleurs" fichiers binaires, vous feriez bien de vérifier comment les itérations successives des compilateurs livrent votre idée du "meilleur" au fil des itérations successives de votre code.

229
Mike Kinghan

Phoronix a fait quelques tests à 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 utilisez le dernier Clang, c'est peut-être un peu moins pertinent. Là encore, il est prévu que GCC 4.6 ait quelques optimisations majeures pour les Core 2 et i7, apparemment.

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

48
Nietzche-jou

Le fait que Clang compile le code plus rapidement n’est peut-être pas aussi important que la vitesse du binaire obtenu. Cependant, voici un série de points de repère .

16
mcandre

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

Les points de repère indiquant qu'il existe un écart de performance important entre GCC et Clang sont une coïncidence.

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

Du point de vue du développeur, une différence notable entre GCC 4.8+ et Clang 3.3 est que GCC dispose de l’option de ligne de commande -Og. Cette option active les optimisations n'interférant 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 en tant que compilateur d'optimisation pour certains développeurs.

12
user811773

La seule façon de déterminer cela est de l'essayer. FWIW J’ai constaté de très bonnes améliorations avec LLVM gcc 4.2 d’Apple par rapport au gcc 4.2 classique (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 devriez également essayer l'ICC d'Intel, car cela peut souvent battre gcc - vous pouvez obtenir une licence d'évaluation valable 30 jours sur intel.com et l'essayer.

9
Paul R

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

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

Ensuite, lors de la compilation avec -O3 ou -O2, gcc déroulera de manière spéculative la boucle huit fois. Clang ne le déroulera pas du tout. Après essais et erreurs, j’ai trouvé que, dans mon cas spécifique, avec les données de mon programme, le nombre correct de déroulements est de cinq, donc GCC est dépassé et nettement inférieur. Cependant, les dépassements étaient plus préjudiciables à la performance, de sorte que gcc a bien moins performé 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 un quelques éboueurs pour en apprendre davantage sur l'optimisation des performances en C. Et les résultats que j'ai obtenus sont suffisamment dans mon esprit pour favoriser légèrement le bruit. D'autant que la récupération de place consiste principalement à poursuivre et à copier la mémoire d'un pointeur.

Les résultats sont (chiffres 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|
+---------------------+-----+-----+

C’est tout du code C pur et je n’affirme en rien les performances des compilateurs lors de la compilation du code C++.

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

7
Björn Lindqvist

Fondamentalement, la réponse est: cela dépend. Il existe de nombreux points de repère axés sur différents types d'applications.

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

Il y a des E/S rares, mais de nombreuses opérations de calcul de flottement du processeur et de structure de données.

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

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

4
kimi