Gentoo Logo

Guide d'optimisation de la compilation

Table des matières :

1.  Introduction

Que sont les CFLAGS et les CXXFLAGS ?

Les CFLAGS et CXXFLAGS sont des variables d'environnement utilisées pour dire au compilateur GNU Compiler Collection, GCC, quels genres de paramètres utiliser quand il compile le code source. Les CFLAGS s'appliquent au code écrit en C alors que les CXXFLAGS s'appliquent au code écrit en C++.

Ces variables peuvent permettre de diminuer le nombre de messages de débogage d'un programme, d'augmenter les niveaux d'alertes d'erreurs et, bien sûr, d'optimiser le code obtenu. Le manuel de GCC maintient une liste exhaustive des options disponibles et de leur utilité.

Comment sont-elles utilisées ?

Les CFLAGS et CXXFLAGS peuvent être utilisées de deux façons. La première est de les définir pour chaque programme avec les fichiers Makefiles générés par automake.

Toutefois, cette méthode ne doit pas être utilisée lors de l'installation de paquets installés depuis l'arbre Portage. À la place de cela, réglez vos variables CFLAGS et CXXFLAGS dans le fichier /etc/make.conf. De cette façon, tous les paquets qui seront compilés utiliseront les options que vous avez spécifiées.

Exemple de code 1.1 : Les CFLAGS dans le /etc/make.conf

CFLAGS="-march=athlon64 -O2 -pipe"
CXXFLAGS="${CFLAGS}"

Comme vous pouvez le voir, la variable CXXFLAGS est paramétrée pour utiliser toutes les options présentes dans la variable CFLAGS. Généralement, ceci vous suffira et ne créera pas de problème. Vous ne devriez pas avoir besoin de spécifier des options supplémentaires dans votre variable CXXFLAGS.

Idées fausses

Bien que les variables CFLAGS et CXXFLAGS peuvent se révéler très utiles pour obtenir du code source plus léger et/ou des binaires plus rapides, elles peuvent aussi détériorer les fonctionnalités de votre code, augmenter son poids, ralentir son temps d'exécution, et même causer des erreurs de compilation !

Les CFLAGS ne sont pas magiques ; elles ne vont pas rendre automatiquement votre système plus rapide ou vos binaires plus légers. Le fait d'ajouter encore et encore des options pour essayer d'optimiser votre système est le meilleur moyen d'obtenir des erreurs. À partir d'un moment, vous serez confronté à la loi des rendements décroissants.

Malgré les vantards que vous trouverez sur Internet, les CFLAGS et CXXFLAGS aggressives sont de loin plus susceptibles de nuire à vos programmes que de les améliorer. Gardez à l'esprit que ces options existent en premier lieu pour être utilisées à des endroits spécifiques et à des fins spécifiques. Le fait qu'une variable CFLAG particulière soit bonne pour un fragment de code ne signifie pas qu'elle soit adaptée à la compilation de tout ce que vous aurez installé sur votre machine !

Prêt ?

Maintenant que vous êtes informé des risques encourus, jetons un coup d'œil à quelques options sûres et raisonnables à utiliser pour l'optimisation de votre ordinateur. Cela vous tiendra en bonne posture et vous fera aimer des développeurs la prochaine fois que vous rapporterez un problème sur le Bugzilla. (Les développeurs vont généralement vous prier de bien vouloir recompiler un paquet avec un minimum de CFLAGS afin de voir si le problème persiste. Souvenez-vous, les options agressives peuvent ruiner le code.)

2.  Optimisation

Les bases

Le but de l'utilisation des CFLAGS et CXXFLAGS est de créer un code conçu spécialement pour votre système ; celui-ci devrait fonctionner parfaitement tout en étant léger et rapide, si possible. Quelques fois ces conditions sont mutuellement exclusives, donc nous allons nous en tenir aux combinaisons connues pour fonctionner correctement. Idéalement, ce sont les meilleures disponibles quelque soit l'architecture du processeur. Nous expliquerons ce que sont les options agressives plus tard ainsi vous saurez ce à quoi il faut faire attention. Nous ne parlerons pas de chaque option listée dans le manuel de GCC (il y en a des centaines), mais nous couvrirons les options classiques, les plus courantes.

Note : À tout moment si vous n'êtes pas sûr de l'utilité d'une option, référez-vous au chapitre approprié du manuel de GCC. Si vous êtes toujours dubitatif, essayez Google ou jetez un œil aux listes de diffusion de GCC.

-march

La première et la plus importante des options est -march. Elle indique au compilateur quel code il doit produire pour l'architecture (ou arch) de votre processeur : elle indique qu'il doit fournir un code pour un certain type de processeur. Les différents processeurs ont des capacités différentes, supportent des jeux d'instructions différents et exécutent le code de façon différente. L'option -march va permettre de signaler au compilateur quel code créer spécifiquement pour votre processeur, avec toutes ses capacités, ses fonctionnalités, ses jeux d'instructions, ses gorges (NdT : "quirks"), et ainsi de suite.

Même si la variable CHOST dans le /etc/make.conf spécifie l'architecture générale utilisée, -march devrait toujours être renseignée pour que les programmes puissent être optimisés pour votre processeur en particulier. Les processeurs x86 et x86-64 (entre autres) doivent utiliser l'option -march.

Quel type de processeur avez-vous ? Pour le savoir, tapez ceci :

Exemple de code 2.1 : Information du CPU

$ cat /proc/cpuinfo

À présent, voyons l'option -march en action. Cet exemple est paramétré pour un vieux processeur Pentium III :

Exemple de code 2.2 : /etc/make.conf: Pentium III

CFLAGS="-march=pentium3"
CXXFLAGS="${CFLAGS}"

Voici un autre exemple pour un processeur AMD 64 bits :

Exemple de code 2.3 : /etc/make.conf: AMD64

CFLAGS="-march=athlon64"
CXXFLAGS="${CFLAGS}"

Si vous n'êtes pas sûr de votre type de processeur, utilisez juste -march=native. Quand cette option est utilisée, GCC détecte votre processeur et ajoute automatiquement les options appropriées. Cependant, cette option ne doit pas être utilisée si vous avez l'intention de compiler des paquets pour un autre processeur !

Donc, si vous compilez des paquets sur un ordinateur et que vous comptez les exécuter sur un autre ordinateur (utiliser un ordinateur rapide pour y compiler des paquets destinés à un vieil ordinateur lent), alors n'utilisez pas -march=native. « Native » signifie que le code produit ne fonctionnera seulement que sur un seul type de processeur. Par exemple, les applications compilées avec l'option -march=native sur un processeur AMD Athlon 64 ne pourront pas fonctionner sur un ancien processeur VIA C3.

Les options -mtune et -mcpu sont également disponibles. Elles ne sont normalement utilisées que s'il n'y a pas d'option -march disponible pour votre processeur ; certaines architectures de processeurs peuvent avoir besoin de -mtune ou même de -mcpu. Malheureusement, le comportement de GCC n'est pas vraiment cohérent avec la façon dont chaque option se comporte d'une architecture à l'autre.

Sur les processeurs x86 et x86-64, l'option -march va générer du code spécifique à tous les processeurs en utilisant tous ses jeux d'instructions disponibles et les bonnes interfaces d'applications binaires (NdT : ABI, pour « Application Binary Interface ») ; il n'y aura pas de problème de compatibilité ascendante pour les processeurs plus vieux ou différents. Si vous n'avez pas besoin d'exécuter du code sur d'autres systèmes que celui sur lequel vous utilisez Gentoo, continuez d'utiliser -march. Vous ne devriez prendre en considération -mtune que si vous devez générer du code pour des vieux processeurs tels que les i386 et i486. L'option -mtune produit du code plus générique que -march ; même s'il va harmoniser le code pour un processeur spécifique, il ne prendra pas compte des jeux d'instructions disponibles et de l'ABI. N'utilisez pas -mcpu sur les systèmes x86 ou x86-64, car c'est déconseillé pour ces architectures.

Seuls les processeurs non-x86/x86-64 (tels que Sparc, Alpha, et PowerPC) peuvent avoir besoin de -mtune ou de -mcpu au lieu de -march. Sur ces architectures, -mtune/-mcpu vont parfois se comporter simplement comme -march (sur x86/x86-64)... mais avec un nom d'option différent. De nouveau, le comportement de GCC et le nommage des options ne sont pas homogènes selon les architectures, donc assurez-vous de bien lire le manuel de GCC pour déterminer celles que vous devez utiliser sur votre système.

Note : Pour plus de suggestions de paramètres pour les options -march/-mtune/-mcpu, veuillez lire le chapitre 5 du Manuel d'installation de Gentoo approprié à votre architecture. De même, lisez la liste des options spécifiques aux architectures dans le manuel de GCC, qui détaillent davantage les différences entre -march, -mcpu, et -mtune.

-O

La variable suivante est -O. Elle contrôle le niveau global d'optimisation. Plus vous augmentrez le niveau d'optimisation, plus la compilation du code prendra du temps et utilisera de la mémoire.

Il y a cinq paramètres -O : -O0, -O1, -O2, -O3, et -Os. Vous ne devrez utiliser qu'un seul d'entre eux dans le fichier /etc/make.conf.

À l'exception de -O0, les paramètres -O activent chacun plusieurs options supplémentaires, donc assurez-vous de bien lire le chapitre sur les options d'optimisation du manuel de GCC pour savoir quelles options sont activées à chaque niveau -O, ainsi que pour obtenir quelques explications sur ce qu'elles font.

Examinons chaque niveau d'optimisation :

  • -O0: Ce niveau (la lettre "O" suivie d'un zéro) désactive entièrement l'optimisation, c'est le niveau par défaut si aucun niveau -O n'est spécifié dans les variables CFLAGS ou CXXFLAGS. Votre code ne sera pas optimisé : ce n'est généralement pas ce qui est voulu.
  • -O1: C'est le niveau le plus classique d'optimisation. Le compilateur va essayer de générer un code plus rapide et plus léger sans prendre plus de temps à compiler. C'est relativement classique, mais cela devrait fonctionner dans tous les cas. que le travail soit fait.
  • -O2: Un étape au-dessus du -O1. C'est le niveau recommandé d'optimisation sauf si vous avez des besoins spécifiques. Le niveau -O2 va activer quelques options en plus de celles du -O1. Avec le niveau -O2, le compilateur va tenter d'augmenter les performances du code sans faire de compromis sur la taille et sans prendre trop de temps à compiler.
  • -O3: Il s'agit du plus haut niveau d'optimisation possible mais aussi du plus risqué. Le temps de compilation sera plus long avec cette option qui en fait ne devrait pas être utilisée de façon globale avec GCC 4.x. Le comportement de GCC a changé de façon significative depuis la version 3.x. Dans la version 3.x, -O3 a montré que son utilisation conduisait à des temps d'exécution marginaux plus rapides qu'avec -O2, mais ce n'est plus le cas avec GCC 4.x. Compiler tous vos paquets avec -O3 produira des plus gros binaires qui demanderont plus de mémoire et va augmenter de façon significative les étranges erreurs de compilation ou provoquer des comportements inattendus pour les programmes (y compris des erreurs). Les inconvénients sont plus nombreux que les avantages ; souvenez-vous du principe des rendements décroissants. L'utilisation du niveau -O3 n'est pas recommandé pour GCC 4.x.
  • -Os: Ce niveau va optimiser votre code en taille. Il active toutes les options du niveau -O2 qui n'influent pas sur la taille du code généré. Il peut être utile pour les machines qui ont une taille très limitée d'espace libre sur le disque dur et/ou qui ont des processeurs avec une petite taille de cache. Toutefois, ce niveau peut tout à fait causer d'autres problèmes, c'est pourquoi il est filtré par beaucoup d'ebuilds dans l'arbre. L'utilisation de -Os n'est pas recommandé.

Comme mentionné précédemment, le niveau -O2 est le niveau requis pour l'optimisation. Si certains paquets créent des erreurs de compilation, assurez-vous que vous n'utilisez pas -O3. Comme option de repli, essayez de régler vos variables CFLAGS et CXXFLAGS a un niveau d'optimisation plus bas, tel que -O1 ou même -O0 -g2 -ggdb (pour les rapports d'erreurs et la vérification des problèmes si possible) et recompilez le paquet.

-pipe

-pipe est une option assez répandue. Elle n'a, en fait, pas d'effet sur le code généré, mais va accélerer le processus de compilation. Elle dit au compilateur d'utiliser des tubes à la place des fichiers temporaires pendant les différentes étapes de la compilation. L'utilisation de tubes va utiliser plus de mémoire. Cette option est donc déconseillée sur les machines avec peu de mémoire, car gcc pourrait en manquer et se faire tuer par le système.

-fomit-frame-pointer

C'est une option très commune créée dans le but de réduire la taille du code généré. Elle est active pour tous les niveaux de -O (à l'exception de -O0) sur les architectures pour lesquelles faire cela ne va pas pas interférer avec le débogage (tels que les x86-64), mais vous pourriez avoir besoin de l'activer vous-même en l'ajoutant à vos options. Bien que le manuel du compilateur GNU GCC ne spécifie pas toutes les architectures sur lesquelles elle est active en utilisant -O, vous aurez besoin de l'activer explicitement sur les architectures x86. Toutefois, l'utilisation de cette option rendra le débogage difficile, voire impossible.

En particulier, cela rend le débogage des applications écrites en Java beaucoup plus difficile, bien que Java ne soit pas le seul code affecté par l'utilisation de cette option. Donc même si cette option peut aider, elle rend le débogage plus délicat : les traçages en particulier seront inutiles. Toutefois, si vous n'avez pas l'intention de faire trop de débogage de logiciels et que vous n'avez pas ajouté d'autres CFLAGS en relation avec le débogage telles que -ggdb, alors vous pouvez essayer d'utiliser -fomit-frame-pointer.

Important : Ne combinez pas -fomit-frame-pointer avec l'option similaire -momit-leaf-frame-pointer. L'utilisation de cette dernière est obsolète, -fomit-frame-pointer fait déjà son travail comme il faut. De plus, -momit-leaf-frame-pointer a montré des impacts négatifs sur les performances du code.

-msse, -msse2, -msse3, -mmmx, -m3dnow

Ces options activent les jeux d'instructions SSE, SSE2, SSE3, MMX, et 3DNow! pour les architectures x86 et x86-64. Elles sont pratiques principalement pour le multimédia, le jeu et d'autres tâches de calcul à virgule flottante, bien qu'elles contiennent plusieurs autres améliorations mathématiques. Ces jeux d'instructions se trouvent dans les CPU les plus modernes.

Important : Assurez-vous de vérifier que votre processeur supporte ces options en exécutant la commande cat /proc/cpuinfo. La sortie va inclure quelques jeux d'instruction supplémentaires supportés. Notez que pni n'est qu'un nom différent pour SSE3.

Normalement vous n'avez pas besoin d'ajouter ces options dans le fichier /etc/make.conf si vous utilisez la bonne option -march (par exemple, -march=nocona implique -msse3). Il y a quelques exceptions notables pour les nouveaux processeurs VIA et AMD64 qui supportent des instructeurs non implicites avec -march (tel que SSE3). Pour les processeurs comme ceux-là, vous devrez activer les options supplémentaires où il faut après avoir regardé la sortie de cat /proc/cpuinfo.

Note : Vous devriez regarder la liste des options x86 et spécifiques aux x86-64 pour voir les jeux d'instructions qui sont activés simplement par l'option du type de processeur. Si une instruction est listée, alors vous n'avez pas besoin de la spécifier, elle sera activée en utilisant le bon paramètre -march.

3.  Questions courantes sur l'optimisation

J'obtenais de meilleures performances avec -funroll-loops -fomg-optimize !

Non, vous pensez juste que c'était le cas parce que quelque vous a persuadé que le fait de mettre plus d'options était une bonne chose. Les options offensives vont seulement nuire à vos applications quand elles sont utilisées à l'échelle du système. Même le manuel de GCC dit que le fait d'utiliser les options -funroll-loops et -funroll-all-loops provoque un alourdissement du code et le rend plus lent à l'exécution. Pourtant, pour une raison quelconque, ces deux options, ainsi que -ffast-math, -fforce-mem, -fforce-addr, et les autres similaires, continuent à être très populaires chez les personnes qui suivent une course à la vantardise.

La vérité sur ce sujet, c'est qu'il s'agit d'options dangereusement aggressives. Jetez un œil sur les forums de Gentoo et au Bugzilla pour voir ce que font ces options : rien de bon !

Vous n'avez pas besoin d'utiliser ces options de manière globale dans les variables CFLAGS et CXXFLAGS. Elles ne feront que nuire à vos performances. Elles peuvent vous faire croire que votre système est à la pointe et a de hautes performances, mais elles ne feront rien d'autre que de gonfler votre code et provoquer des bogues marqués INVALID ou WONTFIX.

Vous n'avez pas besoin d'options dangereuses telles que celles-ci. Ne les utilisez pas. Collez aux options classiques : -march, -O, et -pipe.

Que dire au sujet des niveaux de l'option -O supérieurs à 3 ?

Certains utilisateurs friment d'avoir de bien meilleures performances grâce à l'utilisation de -O4, -O9, et plus encore, mais en réalité les niveaux de l'option -O supérieurs à 3 n'ont aucun effet. Le compilateur peut accepter des CFLAGS telles que -O4, mais en fait il ne va rien en faire. Il n'optimise que pour -O3, rien de plus.

Besoin de plus de preuves ? Regardez le code source de GCC :

Exemple de code 3.1 : Source code de -O

if (optimize >= 3)
    {
      flag_inline_functions = 1;
      flag_unswitch_loops = 1;
      flag_gcse_after_reload = 1;
      /* Allow even more virtual operators.  */
      set_param_value ("max-aliased-vops", 1000);
      set_param_value ("avg-aliased-vops", 3);
    }

Comme vous pouvez le voir, une valeur supérieure à 3 est tout simplement traitée comme un -O3.

Que dire au sujet des options redondantes ?

Souvent, les variables CFLAGS et CXXFLAGS qui sont mises à plusieurs niveaux de -O sont spécifiées comme redondantes dans le fichier /etc/make.conf. Parfois cela se fait par ignorance, mais c'est également fait pour éviter le filtrage des options ou le remplacement des options.

Le filtrage/remplacement des options est réalisé dans beaucoup d'ebuilds de l'arbre Portage. Cela est habituellement dû au fait que les paquets n'arrivent pas à compiler avec certains niveaux de -O ou que le code source est trop facilement alteré avec d'autres options utilisées. L'ebuild va filtrer certaines ou toutes les options CFLAGS et CXXFLAGS, ou alors il les remplacera par un différent niveau -O.

Le manuel du développeur Gentoo explique où et comment le filtrage et le remplacement des options fonctionnent.

Il est possible d'éviter le filtrage des -O par redondance en listant les options pour un certain niveau, tel que -O3, en faisant quelque chose comme cela :

Exemple de code 3.2 : Spécifier les CFLAGS redondantes

CFLAGS="-O3 -finline-functions -funswitch-loops"

Toutefois, ce n'est pas une chose intelligente à faire. Les CFLAGS ont une bonne raison pour d'être filtrées ! Si les options sont filtrées, cela signifie qu'il n'est pas sûr de compiler un paquet avec ces options. Il n'est clairement pas prudent de compiler votre système entier avec -O3 si certaines des options mises par ce niveau vont causer des problèmes avec certains paquets. Ainsi, n'essayez pas d'être plus "fûté" que les développeurs qui maintiennent ces paquets. Faites confiance aux développeurs. Le filtrage des options et leur remplacement est fait dans votre intérêt ! Si un ebuild spécifie des options alternatives, n'essayez donc pas de les contourner.

Vous allez sans doute continuer à rencontrer des difficultés lors de la compilation d'un paquet avec des options inacceptables. Si vous rapportez vos problèmes sur le Bugzilla, les options que vous utilisez dans le fichier /etc/make.conf seront facilement visibles et il vous sera dit de recompiler sans ces options. Épargnez-vous le problème de recompiler en n'utilisant tout simplement pas les options redondantes ! Ne prétendez pas que vous êtes forcément meilleur que les développeurs.

Que dire au sujet des LDFLAGS ?

Les développeurs Gentoo ont déjà mis des paramètres LDFLAGS fondamentaux et sûrs dans les profils de base, vous n'avez donc pas besoin de les changer.

Puis-je utiliser les paramètres définis par paquet ?

Il n'y a pas de méthode qui permettrait de définir des CFLAGS ou d'autres variables par paquet, bien qu'il existe quelques moyens un peu brutaux pour forcer Portage à le faire.

Vous ne devriez pas essayer de forcer Portage à utiliser des variables par paquet, puisqu'il ne le supporte en aucune manière et que cela risque de grandement compliquer les rapports de bogue. Renseignez juste vos variables dans le fichier /etc/make.conf afin de les utiliser de manière globale sur le système.

4.  Ressources

Les ressources suivantes sont d'une certaine utilité pour comprendre l'optimisation :

  • Le manuel de GNU GCC
  • Le chapitre 5 du Manuel d'installation de Gentoo
  • man make.conf
  • Wikipedia
  • Acovea, une suite de test et d'analyse comparative (NdT : "benchmarking") qui peut être utile pour déterminer de quelle façon les différentes options de compilation intéragissent et affectent le code généré, bien sûr ces suggestions de code ne sont pas appropriées pour une utilisation globale. Cette suite est disponible dans Portage : emerge acovea.
  • Les forums Gentoo


Imprimer

Dernière mise à jour le 19 octobre 2009

Une version originale plus récente datée du 26 juillet 2010 existe.

Résumé : Ce guide constitue une introduction à l'optimisation du code compilé en utilisant des variables CFLAGS et CXXFLAGS sûres de manière sensée. Il décrit également la théorie sur laquelle se base l'optimisation en général.

Joshua Saddler
Auteur

Marion Agé
Traducteur

Donate to support our development efforts.

Copyright 2001-2012 Gentoo Foundation, Inc. Questions, Comments? Contact us.