Journal Clang++ est prêt

Posté par  (site web personnel) .
Étiquettes :
32
24
mai
2010
Bonjour,

Ce week-end, alors qu'il m'était impossible de réactualiser frénétiquement la page d'accueil de Linuxfr pour voir s'il n'y avait rien d'intéressant, je suis allé sur le site de LLVM.

Tout d'abord, la page d'accueil de ce site est un peu plus peuplée, et liste tous les projets intéressantes de LLVM :

  • LLVM lui-même, architecture modulaire permettant de créer des compilateurs pour plein de langages, mais aussi des interpréteurs, etc
  • Clang, le compilateur C, C++, Objective-C et Objective-C++ se basant sur LLVM pour la génération de code.
  • llvm-gcc et DragonEgg, deux projets visant à intégrer LLVM dans GCC, donc de se servir des front-ends de GCC et de LLVM pour générer le code. llvm-gcc est un fork de GCC 4.2, DragonEgg est un plugin pour GCC 4.5.
  • libc++, projet jeune et très intéressant, conciste à une réécriture de la libstdc++, sous licence LLVM. Le projet est complet à 80% (en comptant les nouveautés du C++0x). Cette réécriture est en particulier demandée par l'arrivée de cette nouvelle version du C++, qui oblige des changements de fond dans les bibliothèques C++. Au lieu de hacker la libstdc++ de GCC, les développeurs de LLVM préfèrent en recréer une nouvelle, tirant parti des découvertes et inventions faites dans le domaine depuis les dernières années.
  • compiler-rt, équivalent LLVM de la libgcc. Cette petite bibliothèque contient des fonctions souvent appelées dans le code, permettant d'émuler des opérations qu'un certain target ne supporte pas. Par exemple, il est difficile pour certains processeurs de convertir un nombre flottant sur 32 bits (float) en un double (64 bits).
  • vmkit, essai visant à émuler une machine virtuelle Java ou .NET au-dessus de LLVM. La machine virtuelle Java fonctionne déjà plus ou moins et sait lancer Eclipse, Tomcat, etc.
  • Klee est un genre d'analyseur de code. On compile son code, on le lance dans Klee, et il nous dit toutes les valeurs que peuvent prendre les variables. Ça permet de voir s'il n'y a pas de pointeurs NULL qui se baladent, pas de code inutilisé, combien de fois une boucle est utilisée, etc.

Mais ce qui m'intéresse ici est Clang, le compilateur pour les langages de la famille du C.

Comme je l'avais dit dans mes précédant journaux, Clang essaie de supporter le C++, ce qui est difficile étant donné la complexité atroce de ce langage pour le compilateur.

Hier, je me balade donc sur le site de Clang. Je clique sur la page Statut du C++, et voit qu'elle est quasiment vide.

Auparavant, elle contenait une centaine (voire plus) de lignes dans un tableau, une pour chaque test du standard C++ 2003. Au fur et à mesure que le temps passait, des tests passaient en vert, ou du rouge vers l'orange quand ils étaient implémentés.

Ce tableau n'existe plus pour le C++ standard, il n'en reste qu'un petit morceau pour le futur C++-0x. Je lis donc attentivement l'introduction de cette page et découvre ceci :

Clang currently implements all of the ISO C++ 1998 standard (including the defects addressed in the ISO C++ 2003 standard) except for 'export' (which has been removed from the C++'0x draft). However, the implementation of Clang C++ is still somewhat immature, with remaining bugs that may cause compiler crashes, erroneous errors and warnings, or miscompiled code. The LLVM bug tracker contains a Clang C++ component that tracks known Clang C++ bugs.

Ou pour les anglophobes :

Clang implémente pour le moment tout le standard ISO C++ 1998 (incluant les défauts corrigés dans le standard ISO C++ 2003), sauf 'export' (qui a été retiré du brouillon du C++-0x). Cependant, l'implémentation du C++ de Clang est encore un peu immature, avec des bugs qui peuvent causer des crashs du compilateur, de fausses erreurs et warnings, ou un code mal compilé. Le bug tracker de LLVM contient un composant Clang C++ permettant de traquer les bugs connus du C++ de Clang

(désolé pour la traduction pourrie, c'est du fait-main et je ne traduis pas souvent).

Il s'avère donc que Clang supporte tout le C++, sauf quelques extensions (listées sur une page spéciale).

Je m'arme donc d'un programme de test en C++, j'ai nommé Setup, le gestionnaire de paquets que je développe. Il est composé de 15k SLOC C++, utilisant Qt, des templates, toute forme d'héritage, le préprocesseur, les bindings C (extern "C"), la surcharge des opérateurs, etc. Une bonne partie du C++ (mais pas les exceptions).

Je lance donc Clang dessus, avec un simple «cmake -DCMAKE_CXX_COMPILER="clang++"». Je lance make, ça marche.

Out-of-the-box en plus ! Rien à faire ! C'est un succès total. Clang me génère même des warnings que GCC ne me trouvait pas. Je les ai corrigés.

Setup se lance, fait tout ce que je lui demande, et bien. Clang a vaincu un programme complexe, architecture bibliothèque-clients, utilisant Qt.

Je regarde un peu le blog de LLVM et vois que Clang compile impeccablement la bibliothèque C++ Boost, réputée pour sa complexité. Son support du C++ est donc devenu excellent.

Benchmarks

Maintenant, place aux benchs. Tout d'abord, j'ai consulté cette page, une page pour les hardcore hackers (oui oui), qui décrit comment créer un plugin pour LLVMC.

LLVMC est comme GCC, c'est à dire un directeur de compilation (compiler driver). C'est lui qui parse les options qu'on lui donne, et décide quels programmes appeler. Par exemple, on peut générer des fichiers objets à partir de fichiers en C, mais on peut aussi lier les fichiers objets en un exécutable.

LLVMC fonctionnait déjà avant mon intervention, mais pas comme je voulais. Il se comportait trop comme GCC, compilant le code en dur, le liant en utilisant LD, etc.

Pour mes tests, je l'ai modifié pour utiliser 100% LLVM. Les fichiers C sont compilés en fichiers bitcode LLVM, sortis sous forme de .o (mais qui ne sont pas des .o normaux). Le lieur n'est pas LD, mais llvm-link.

Le but du machin est du tirer au maximum parti de LLVM et de Clang. Voici théoriquement comment se déroule la compilation :

* Clang compile les fichiers .c, .cpp, .m et .mxx en fichiers .o, contenant du bitcode LLVM non-optimisé. Les options -Ox sont ignorées ici.
* llvm-link lie ces fichiers .o en un gros fichier .o, toujours non-optimisé.
* Si on a précisé une option -Ox, alors opt (l'optimiseur de LLVM) est appelé sur ce fichier .o, et génère un fichier .o optimisé. Le but de la manoeuvre est d'optimiser une seule fois (plus rapide), et surtout sur un seul fichier. On a ainsi gratuitement une Link Time Optimization, beaucoup plus efficace et agressive qu'une optimisation après chaque compilation.
* La sortie de opt, ou de llvm-link si on n'optimise pas, est fournie à llvm-ld. Ce programme sort un script shell, lançant l'interpréteur LLVM sur un fichier .bc, contenant le bitcode complet du programme. Il est également possible de passer l'option "-native" à LLVMC, qui la passera à llvm-ld, qui sortira alors un beau fichier ELF comme on a l'habitude.

Chaîne de compilation relativement classique, et plus simple que celle de GCC (qui appelle tout un tas de lieurs, assembleurs, collect2, etc).

Les chiffres maintenant. Comme toujours, je me sers de Cream, un navigateur web léger développé par une connaissance (oui je fais de la pub, mais j'aime bien ce navigateur).

C'est du C, pas du C++, mais il utilise GTK et Webkit. Il est donc intéressant. Les tests sont faits sur un Packard Bell DOT/MA.FR-30, c'est à dire un Netbook avec un disque dur 5200tpm de 160Gio, 1Gio de RAM DDR2 660Mhz, un processeur AMD Athlon 64 L110 à ... 1,2Ghz, 512Kio de cache, carte graphique ATI Radeon X1270 (RS690G). Bref, une bouze niveau rapidité, mais il a un magnifique écran 1366x768 (troll: et KDE 4.4 tourne comme un charme dessus, et se lance en quelques secondes) [/mavie].

  • Compilé avec gcc, en -g -O2 (options standard des autotools), la compilation prend 16,715 secondes.
  • Compilé avec gcc -flto, toujours en -O2, sans le -g (incompatible avec -flto), la compilation crashe après 17,670 secondes.
  • Compilé avec llvmc (Clang donc, relisez ce que je met plus haut pour comprendre), toujours en -g -O2, la compilation prend 10,295 secondes. Simplement, j'ai alors un fichier bitcode, pas un exécutable.
  • Compilé avec llvmc -native, en -g -O2, la compilation prend 12,166 secondes, et j'ai un fichier ELF fonctionnant parfaitement.

Niveau vitesse de compilation, il n'y a pas photo. GCC prend près de 17 secondes, Clang en prend 12, alors qu'il utilise des optimisations plus agressives, faites au moment de la liaison finale.

Dans les deux cas, Cream est parfaitement fluide et affiche sans problème n'importe quel site web.

Je referai bientôt des tests plus poussés du C++, en compilant Blender par exemple, pour voir ce que ça donne. Je dois d'abord corriger quelques problèmes, en particulier le fait que les outils que j'utilise (CMake, les autotools) ne sont pas habitués à avoir du bitcode LLVM à la place du ELF. Il y a plein de strip partout, les fichiers compilés sont lancés, etc.

Voilà. Journal un peu long, mais j'ai dit tout ce que j'avais à dire.
  • # un modo dans les parages?

    Posté par  . Évalué à 10.

    Je pense qu'il faut cliquer sur le bouton "transformation en depeche". Merci de ce journal tres interessant.
    • [^] # Re: un modo dans les parages?

      Posté par  (site web personnel) . Évalué à 4.

      Merci :) .

      Mais la partie «dépêche» est peut-être un peu courte (juste le début, présentation de LLVM, et l'annonce que Clang supporte le C++).

      Sinon, pour ceux qui voudraient reproduire mes benchmarks, il faut suivre la documentation que j'ai donné près du lien «pour les hardcore hackers». Dans ce lien, ils vous parleront d'un fichier «tablegen». Au lieu de vous arracher les cheveux à retrouver comment j'ai fait, voici le mien : http://logram-project.org/pastebin-3-n49c4d9a46717.html .

      D'ailleurs, j'ai réussi à compiler Neverball avec :) . Ça marche très bien et il est jouable en 640x480 toutes options désactivées (on sent la carte ATI intégrée avec pilotes libres et le L110 ici, gcc faisant la même chose).
    • [^] # Re: un modo dans les parages?

      Posté par  . Évalué à 4.

      avant la transformation en dépêche, il faut changer "conciste" en consiste, j'ai les yeux qui piquent... xD
  • # Bench entre GCC et LLVM

    Posté par  (site web personnel) . Évalué à 9.

    Tiens ton journal me donne l'occasion de parler d'un bench sérieux qui a été publié à propos de GCC 4.5 et LLVM 2.7.
    Je voulais faire une news en testant les deux dernières versions des compilateurs mais, accablé par mon incompétence, j'avais abandonné l'idée...et hop ! Une vraie comparaison a été effectuée par Vladimir Makarov et les résultats postées sur la mailing list de GCC => http://gcc.gnu.org/ml/gcc/2010-04/msg00948.html

    Les résultats détaillées sont ici => http://vmakarov.fedorapeople.org/spec/

    Comparaison des différentes versions de GCC => http://vmakarov.fedorapeople.org/spec/comparison64.html
    Comparaison entre GCC 4.5 et LLVM 2.7 => http://vmakarov.fedorapeople.org/spec/llvmgcc64.html

    Attention a prendre le temps de bien lire les benchs puisque les tableaux visualisent trois choses différentes successivement :
    1) Compilation Speed
    2) Code Size
    3) Generated Code Performance

    Pour les décideurs pressés :
    1) GCC 4.5 est plus lent que LLVM 2.7 pour compiler (entre 10 et 15%)
    1) GCC 4.5 génère du code plus rapide que LLVM 2.7 (environ 7% sur les benchs des entiers et environ 15% sur les benchs des flottants)
    • [^] # Re: Bench entre GCC et LLVM

      Posté par  . Évalué à 1.

      Merci pour les infos, c'est justement la performance du code généré qui me semble plus importante que la vitesse de compilation (on utilise plus souvent un programme qu'on le compile ;-) )

      C'est néanmoins bien parti pour qu'ils "explosent" les performances de GCC.
      • [^] # Re: Bench entre GCC et LLVM

        Posté par  (site web personnel) . Évalué à 6.

        >>> C'est néanmoins bien parti pour qu'ils "explosent" les performances de GCC

        mmm...qu'est-ce qui te fait dire ça ?
        • [^] # Re: Bench entre GCC et LLVM

          Posté par  . Évalué à 0.

          Bien que l'implémentation LLVM soit "jeune", ils ont déjà des performances proches. J'imagine donc qu'ils ont encore de la "marge" sur ce qu'ils peuvent faire, même si je n'ai aucune preuve pour l'étayer.
          • [^] # Re: Bench entre GCC et LLVM

            Posté par  . Évalué à 10.

            Le problème, c'est que souvent, les améliorations de performances quand elles ne sont pas asymptotiques sont logarithmiques. Il est donc probable que le retard vis-à-vis de GCC soit comblé de moins en moins vite.

            « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

      • [^] # Re: Bench entre GCC et LLVM

        Posté par  . Évalué à 6.

        Merci pour les infos, c'est justement la performance du code généré qui me semble plus importante que la vitesse de compilation

        On pourrait imaginer utiliser LLVM pendant le développement pour pouvoir tester le programme plus vite et le compiler avec GCC quand c'est fini.

        « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

    • [^] # Re: Bench entre GCC et LLVM

      Posté par  (site web personnel) . Évalué à 3.

      Bonjour,

      C'est effectivement intéressant, mais il me semble (ce n'est pas clairement marqué, je me base sur un «LLVM GCC frontend») qu'ils utilisent llvm-gcc pour leurs tests, pas Clang.

      Et la vitesse gagnée vient justement de Clang, car c'est la partie «font-end» de GCC qui est lente. Il faut également voir si Clang ne produit pas du code que LLVM sait mieux optimiser.

      J'avais une fois fait un test sur un programme moyen (400 lignes de C, mais un truc tordu qui construit un suffix-tree puis fait plein d'opérations dessus), et ce programme compilé avec Clang s'exécutait en un peu moins de 0,9 secondes, et un peu plus de 1,1 seconds avec GCC.

      Je devrai aussi tester tout ça, avec mon support des optimisations à la liaison, sur un programme qui mouline bien (genre Lame, et ceux que Phoronix utilise pour ses tests de compilateurs). J'ai déjà essayé avec Yafaray, mais il ne veut pas se compiler, même avec GCC. Je donnerai des nouvelles.
      • [^] # Re: Bench entre GCC et LLVM

        Posté par  . Évalué à 2.

        Ou alors tu vas directement voir sur le site de Phoronix : Benchmarking LLVM & Clang Against GCC 4.5.
        • [^] # Re: Bench entre GCC et LLVM

          Posté par  . Évalué à 3.

          • [^] # Re: Bench entre GCC et LLVM

            Posté par  (site web personnel) . Évalué à 3.

            J'ai déjà lu ça, mais ce qui m'intéresse, c'est de voir comment se débrouille mon LLVMC qui est quand-même intéressant du fait qu'il n'optimise qu'une seule fois (la compilation est extrêmement rapide, ça défile aussi vite qu'un simple sed un peu recherché).

            Le fait que l'optimisation soit faite sur le bytecode final permet de mieux optimiser, d'inliner des fonctions, de propager des constantes, etc. Le LTO, ça marche vraiment, même si on se dit que ça ne sert que dans peu de cas.

            Exemple, j'ai dans Setup une fonction nommée matchVersion, qui vérifie qu'une version correspond à une autre comme on veut (en clair, si on lui passe "1.3.4", >=, "1.3.0", elle retourne true). Le truc que j'envoie au milieu est un simple INT, représentant l'opération.

            Une partie de mon écriveur de base de donnée s'occupe de rechercher une version exacte. Il envoie donc toujours DEPEND_OP_EQ (le ==). Problème, sans LTO, matchVersion est dans un autre fichier.

            Avec LTO, LLVM me détecte que j'envoie une valeur immédiate. Il va alors m'inliner cette fonction, me retirer les ifs qu'il y a dedans, retirer la fonction que je n'appelle donc pas, supprimer une variable inutilisée, inliner une autre fonction dedans, en appliquant le même traitement.

            Finalement, un «setup update» passe de 1,2 secondes à 0,8. Pas mal non ?

            Alors oui, Clang supporte l'option -O4 qui fait aussi du LTO .. uniquement sur Darwin. En effet, le toolchain Clang sous Linux est encore minimal (en gros : passer le plus vite possible en ELF et appeler les outils de GCC), le gain est donc minimal. Mon LLVMC reste le plus longtemps en bitcode LLVM, et compare donc bien mieux les différences entre GCC et LLVM.
            • [^] # Re: Bench entre GCC et LLVM

              Posté par  . Évalué à 1.

              Lors des mes études, il y avait trois types de fichiers. Les source en .cxx, les entêtes en .h, et les sources des fonctions simples en .hxx.

              Les .hxx étaient inclus dans les .h, et donc le code des fonctions simples était inclus dans tout les fichiers, permettant au compilateur de faire les inline qui font bien.

              Envoyé depuis mon lapin.

              • [^] # Re: Bench entre GCC et LLVM

                Posté par  . Évalué à 1.

                Ça a des inconvénients par rapport au LTO : Ça allonge le temps de compilation, a chaque fois que tu modifies un .h tu dois tout recompiler, et ça augmente la taille du code final (à chaque fois q'une fonction d'un header n'est pas inlinée ça fait une fonction statique).
  • # Coupures volontaires

    Posté par  . Évalué à 10.

    Ce week-end, alors qu'il m'était impossible de réactualiser frénétiquement la page d'accueil de Linuxfr pour voir s'il n'y avait rien d'intéressant, je suis allé sur le site de LLVM.

    Cela me fait penser qu'il serait peut-être bien que linuxfr soit indisponible 24 heures aléatoirement tous les x jours.
    'n'est pas une bonnidée ça ? Hum ?
    Non, vraiment pas ?
  • # optimisation globale

    Posté par  . Évalué à 8.

    Si on a précisé une option -Ox, alors opt (l'optimiseur de LLVM) est appelé sur ce fichier .o, et génère un fichier .o optimisé. Le but de la manoeuvre est d'optimiser une seule fois (plus rapide),

    Tu as oublié un truc quand même... Si tu recompiles seulement un fichier C (suite à petite modif par exemple), l'optimisation devra être refaite sur l'intégralité du programme, et là ce n'est pas plus rapide, mais a priori beaucoup plus lent que si l'optimisation était faite sur chaque fichier séparé.
    • [^] # Re: optimisation globale

      Posté par  . Évalué à 3.

      certes, mais alors et en général c'est que tu es encore en train de bidouiller sur ton projet, donc les optimisations tu t'en fous pour l'instant
      • [^] # Re: optimisation globale

        Posté par  . Évalué à 4.

        Pas forcément, si les bidouilles que tu fais sont précisément des améliorations de performances. Ou des corrections de bugs liés aux dites améliorations.
  • # ...

    Posté par  . Évalué à 2.

    pour compléter : clang++ compile boost depuis peu (voir leur blog).


    libc++ n'implémente que la lib standard c++, libstdc++ implémente aussi la stl ?
    le choix de réécrire ces libs, c'est aussi une question de licence : bsd vs (l)gpl.

    Mais oui le projet llvm à une bonne dynamique et est en train de rattraper gcc (au moins pour le platforme classique X86 mac osx/linux).
    • [^] # Re: ...

      Posté par  (site web personnel) . Évalué à 5.

      > le choix de réécrire ces libs, c'est aussi une question de licence : bsd vs (l)gpl.

      En fait, plus précisément BSD Vs GPLv3 : les employés Apple n'ont pas le droit de bosser sur du code GPLv3 :

      http://thread.gmane.org/gmane.comp.compilers.llvm.devel/3174(...)
      « It doesn't really matter. It is widely known that Apple employees are generally not allowed to touch GPL3
      code. The reasons and decision was not an engineering choice. »

      Les détails sur le choix de démarrer libc++ :

      http://thread.gmane.org/gmane.comp.compilers.llvm.devel/3174(...)
    • [^] # Re: ...

      Posté par  (site web personnel) . Évalué à 5.

      > libc++ n'implémente que la lib standard c++, libstdc++ implémente aussi la stl ?

      Vu que STL signifie « Standard Template Library », ce n'est pas une surprise que la STL fasse partie de la lib standard C++. J'ai pas regardé en détails, mais dans les sources de la libc++, il y a des fichiers include/vector, include/algorithm et autres qui semblent confirmer que la libc++ implémente la STL (enfin, modulo le fait que libc++ n'est pas terminée).
      • [^] # Re: ...

        Posté par  . Évalué à 2.

        Sur le site de libc++ on peut lire :

        libc++ is still under development. It has about 85% of N3092 implemented/tested. C++'98 support is fully featured, and most of C++'0x support is as well. The only major missing pieces of C++'0x support are and , and parts of .

        Donc il ne manque vraiment pas grand chose. Actuellement peu des fonctionnalités de C++0X sont employées car pas encore standardisées. Je vois qu'il y a quand même pas mal de fonctionnalités de TR1 qui sont déjà implémentées.
        Bref, ils avancent très vite.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.