Guide d'optimisation de la compilation
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
Ce document est protégé par la licence Creative
Commons : Paternité - Partage des Conditions Initiales à
l'Identique 2.5.
|