I. Problèmes▲
Rendre une fonction inline augmente-t-il son efficacité ?
Quand et comment décider de rendre une fonction inline ?
II. Solution▲
II-A. Écrivez ce que vous savez, sachez ce que vous écrivez▲
Rendre une fonction inline augmente-t-il son efficacité ?
Pas nécessairement. Avant tout, si vous avez essayé de répondre à cette question sans vous demander tout d'abord ce que vous vouliez optimiser, vous êtes tombé dans un piège classique. La première question doit être : « Qu'entendez-vous par efficacité ? » Cela signifie-t-il minimiser la taille du programme ? L'empreinte mémoire ? Le temps d'exécution ? Maximiser la vitesse de développement ? Le temps de compilation ? Autre chose ?
Ensuite, contrairement à une opinion répandue, mettre inline peut améliorer OU aggraver l'un de ces facteurs :
- la taille du programme : beaucoup de développeurs supposent que définir une fonction inline augmente la taille des programmes, parce qu'au lieu d'avoir une seule copie d'un code de fonction, le compilateur en crée une copie à chaque site d'appel de la fonction. C'est souvent vrai, mais pas toujours : si la taille de la fonction est inférieure à celle du code que le compilateur doit générer pour appeler la fonction, rendre inline réduira la taille du programme ;
- l'empreinte mémoire : habituellement, mettre inline a peu ou pas d'effet sur l'utilisation de la mémoire par le programme, excepté au niveau de la taille de base du programme (voir plus haut) ;
-
le temps d'exécution : beaucoup de développeurs présument que
rendre inline une fonction améliore son temps d'exécution,
parce que cela évite la perte de temps de l'appel de fonction
et parce que « voir à travers le voile » de l'appel de fonction
donne à l'optimisateur de compilation davantage de possibilités.
Cela peut être vrai, mais souvent ça ne l'est pas : si la fonction
n'est que rarement appelée, il n'y aura habituellement pas
d'amélioration visible du temps global d'exécution du programme.
En fait, il peut même se produire exactement l'inverse : si
rendre inline augmente la taille de la fonction appelante, il
réduit la localité des références de la fonction appelante,
ce qui signifie que la vitesse globale du programme peut être
de fait réduite si la boucle interne de la fonction appelante
ne correspond plus au cache du processeur.
Pour mettre ce point en perspective, n'oubliez pas que la plupart des programmes ne sont pas liés au CPU. Il est probable que le goulot d'étranglement le plus courant soit la liaison entrée/sortie, ce qui peut inclure n'importe quoi, depuis la bande passante du réseau au délai d'attente pour l'accès à un fichier ou à une base de données ; - la vitesse de développement et le temps de compilation : pour être le plus utile possible, le code inline doit être visible pour l'appelant, ce qui signifie que l'appelant doit dépendre du contenu du code inline. Dépendre des détails internes d'implémentation d'un autre module augmente en pratique le couplage des modules (cela n'augmente néanmoins pas leur couplage théorique, parce que l'appelant n'utilise en fait pas le contenu des modules appelés). Habituellement, quand les fonctions sont modifiées, il n'est pas nécessaire de recompiler les appelants (seuls leurs liens sont révisés. Et encore !). Quand des fonctions inline sont modifiées, les appelants sont obligés d'être recompilés.
Finalement, si vous cherchez à améliorer l'efficacité, commencez toujours par vous pencher sur vos algorithmes et vos structures de données, ils vous donneront des améliorations globales de plusieurs ordres de grandeur, alors que les optimisations de processus, comme mettre inline donneront généralement (notez bien « généralement ») des résultats moins probants.
II-B. Sachez dire « Pas maintenant »▲
Quand et comment décider de rendre une fonction inline ?
Comme pour n'importe quelle autre optimisation : lorsqu'un profileur vous dit de le faire et pas avant. Les seules occasions où vous pourriez sans hésitation rendre inline, c'est avec une fonction vide qui restera probablement vide, ou si vous êtes absolument contraint de le faire, en général quand vous écrivez un template non exporté.
En définitive, rendre inline a toujours un coût, au minimum d'augmenter le couplage, et vous ne devriez jamais payer pour quoi que ce soit sans être assuré d'en tirer profit.
« Mais je peux toujours dire : où se trouvent les goulots d'étranglement » vous dites-vous ? Ne vous inquiétez pas, vous n'êtes pas seul. La plupart des développeurs pensent cela un jour ou l'autre, mais ils ont tort. Sur toute la ligne. Il est notoire que les développeurs ont du mal à localiser les goulots d'étrangement présents dans leur code.
Habituellement, seules les preuves expérimentales (profilage) vous aident à dire où se situent les vrais points chauds. Neuf fois sur dix, un développeur ne peut pas identifier le principal goulot d'étranglement dans son code sans avoir recours à un outil de profilage. Après plus d'une décennie dans ce business, j'attends encore de voir un développeur avec qui j'aurais travaillé ou dont j'aurais entendu parler et qui échapperait à cette règle, même si tout un chacun peut toujours proclamer haut et fort que cette règle ne s'applique pas à lui.
[Notez une autre raison pratique à cela : les profileurs ne sont pas aussi bons pour identifier les fonctions inline qui ne DOIVENT PAS être inline.]
II-B-1. Qu'en est-il des tâches à forte densité de calcul (ex. bibliothèques numériques) ? ▲
Certaines personnes rédigent des codes de bibliothèque petits et compacts, telle que des bibliothèques avancées de calculs scientifiques et d'ingénierie, et s'en sortent parfois assez bien pour rendre inline. Mais même ces développeurs ont tendance à mettre inline judicieusement et à faire les ajustements plutôt tard que tôt. Notez que rédiger un module puis comparer les performances avec « inlining on » et « inlining off » est généralement une mauvaise idée, parce que « all on » ou « all off » est une mesure grossière qui ne vous parlera que du cas moyen : ça ne vous dira pas QUELLES fonctions inline en ont bénéficié (ni dans quelle mesure). Et même dans ces cas, vous avez habituellement plutôt intérêt à utiliser un profileur et à optimiser votre code en vous basant sur ses conseils.
II-B-2. Qu'en est-il des accesseurs ? ▲
D'aucuns m'objecteront que les fonctions accesseurs sur une ligne (du genre X& Y::f() { return myX_; }) sont une exception et pourraient/devraient raisonnablement être systématiquement inline. Je comprends le raisonnement, mais faites attention : en fin de compte, tout code inline augmente le couplage, aussi à moins que vous soyez d'avance certain que mettre inline vous aidera, cela ne coûte rien de repousser cette décision au moment du profilage. Par la suite, quand le code sera stable, un profileur pourra indiquer que inline est utile et à ce moment : a) vous saurez que ce que vous faites vaut la peine d'être fait ; b) vous aurez évité tous les couplages et les possibles pertes de temps jusqu'à la fin du cycle de développement de projet. Pas mal, non ?
III. En résumé▲
À partir des normes de codage GotW :
- évitez de mettre inline et de faire tout réglage fin tant que les profils de performances n'ont pas prouvé le besoin (Cline95 : 139-140, 348-351 ; Meyers92 : 107-110 ; Murray93 : 234-235; 242-244) ;
- corollaire : en général, évitez de mettre inline (Lakos96 : 631-632 ; Murray93 : 242-244) ;
- Cline95 : Marshall Cline and Greg Lomow. "C++ FAQs" Addison-Wesley, 1995 ;
- Lakos96 : John Lakos. "Large-Scale C++ Software Design" Addison-Wesley, 1996 ;
- Meyers92 : Scott Meyers. "Effective C++" Addison-Wesley, 1992 ;
- Murray93 : Robert Murray. "C++ Strategies and Tactics" Addison-Wesley, 1993.
[Notez bien qu'il est bien dit « évitez » (et non pas « ne faites jamais ») et que ces mêmes normes de codage encouragent de mettre inline très tôt dans une ou deux situations restreintes.]
IV. Remerciements▲
Cet article est une traduction par l'équipe de la rubrique C++ de l'article de Herb Sutter publié sur Guru of the Week. Vous pouvez retrouver cet article dans sa version originale sur le site de Guru of the Week : InlineInline.
Merci à Bousk et Luc Hermitte pour leur relecture technique, à ClaudeLELOUP, à jacques_jean et à oodini pour leur relecture orthographique.