c'est toujours d'actualité : je conseille de l'utiliser.
c'est devenu obsolète : à oublier !
C'est plus que le compilateur ne laisse pas forcément ces informations ! GCC est bien connu pour ça, même le débuggage de programmes en O3 peut être pénible. En revanche, les infos de Visual Studio sont toujours complètes (au moins de ce que j'ai pu voir).
Enfin, quand tu codes proprement et que tu as des boucles sur des millions d'éléments, tu es bien content de la suppression des appels, car un appel signifie sauvegarde des registres, passage des arguments sur la pile, et idem à la sortie. Quand ta fonction interne est courte, tu peux avoir des facteurs d'accélération impressionnants.
Au fait, question bête, tu as répondu quoi à "virtual inline" ?
Sauf que tu ne peux jamais être certain de l'inlining, la directive pouvant être ignorée par le compilateur. La seule chose sûre avec l'inlining, c'est que tu peux garantir (par l'absence de directive et les options de compilation adéquates) qu'une fonction que tu ne veux pas inliner ne le sera effectivement pas... Ceci n'a toutefois guère d'intérêt de façon générale, sauf si l'on doit compiler en faveur de la taille du code binaire bien sûr (ce qui est un cas relativement rare, tout de même).
Du coup, si tu as un code réellement critique à optimiser, où l'inlining offre réellement des gains mesurables, il vaut souvent mieux transformer ta fonction inline en bonne vieille macro... C'est la seule manière d'assurer à 100% une traduction en inline quel que soit le compilateur et/ou les flags de compilation.
Franchement, le compilateur sait très bien faire tout seul cela (comme je l'ai déjà dit, sur un code critique, l'inlining automatique m'a fait gagné un facteur 10), je ne vois pas l'intérêt de perdre le typage pour introduire des erreurs (surtout que si c'est une macro, soit c'est suffisamment court pour que le compilo inline à coup sûr, soit c'est trop gros pour que ça ne soit pas dangereux d'être une macro).
Je me pose une question à propos de ces inline.
Etant donné que :
préciser qu'une fonction est inline n'est qu'une suggestion au compilateur, qui n'est pas obligé de la suivre
quand on ne précise pas inline, le compilateur peut tout de même rendre la fonction inline si il trouve cela judicieux
Du coup, quel est l'intérêt de préciser inline puisque le compilateur n'en fait de toute façon "qu'à sa tête" ? (j'exagère un peu, je me doute que quand on lui suggère il y a plus de chances que la fonction devienne inline que quand on ne lui suggère pas, néanmoins inline reviendrait en fait à dire, "augmente de 15% la probabilité que cette fonction devienne inline" ?)
Corrigez moi si je me trompe.
Le seul intérêt est qu'il n'exporte pas le nom de la fonction en dehors de l'unité de compilation. Donc ça permet de pallier à des erreurs de conception.
Il y a des choses qui m'échappent :
- si le compilateur ne suit pas le conseil qu'on lui donne de mettre une fonction inline, le nom de la fonction sera "exporté en dehors de l'unité de compilation" non ?
- pourquoi le fait de ne pas "exporter le nom de la fonction en dehors de l'unité de compilation" pallie les erreurs de conception ?
Edit: c'est la fin de la semaine, j'avais écrit volatile à la place de inline...
Je parle bien de code réellement critique, pas juste de l'inlining automatique "classique".
De façon générale, tu as raison, et/ou il peut être intéressant de voir les options spécifiques des compilateurs (à condition qu'ils en aient, cependant).
Passer en macro, c'est surtout pour passer outre tout flag de compilation et/ou spécificités du compilateur. Comme cela peut être difficile à maintenir, c'est bien entendu à réserver aux cas très pointus le demandant.
Mais il y a des fonctions qui ont vocation à être dans un en-tête: Notamment, certaines fonctions utilisées en complément d'une macro (ben oui, on ne peut pas toujours se débarrasser complètement de ces dernières).
Et puis, ne pas oublier que tout le monde ne compile pas en LTCG, sans compter les cas où la LTCG ne sert à rien (distribution de bibliothèque). Dans ces cas, l'inlining automatique n'est pas possible, et seule reste l'option de définir la fonction en inline dans un header...
"Simplement" parce que l'on a trop souvent tendance à perdre demeter de vue...
Lorsque tu crées une classe ou une fonction template, nous sommes d'accord avec le fait que c'est pour te permettre de l'adapter en fonction du type de donnée réellement manipulée.
Mais les manipulations adaptées à une données particulière ne devrait se trouver que dans une unité de compilation donnée, les autres unités de compilation devraient se "contenter" de savoir que l'on peut effectivement utiliser la fonction.
Dans l'idéal, tu devrais donc avoir une séparation claire entre la déclaration des fonctions template et leur implémentation.
Pour la grosse majorité des unités de compilation, seule la connaissance de l'existence des classes et des fonctions est suffisante, l'implémentation ne les intéressant normalement pas.
Un fichier d'en-tête proche de
MyClass.h
Tu aurais une implémentation séparée proche de
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 /* ce fichier ne fait que déclarer les fonctions et définir la classe, et ce * devrait être suffisant pour une grosse majorité des unités de compilation */ template <class T> class MyClass { public: MyClass(); ~MyClass(); void foo(); static void bar(); private: T data; int truc; static Type machin; };
MyClass.tpp
Et enfin, tu aurais une unité de compilation qui manipule explicitement MyClass en déterminant le type de T et étant seule responsable de la gestion de la spécialisation effective:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 //il faut bien que le compilateur connaisse la classe #include <MyClass.h> template <class T> Type MyClass<T>::machin=Type(); template <class T> MyClass<T>::MyClass():data(),truc(0){} template <class T> MyClass<T>::~MyClass(){} template <class T> void MyClass<T>::foo() { /*...*/ } template <class T> void MyClass<T>::bar() { /*...*/ }
OtherClass.h
OtherClass.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 /* selon le cas, l'un ou l'autre sont envisageable */ template <class T> class MyClass; class OtherClass { public: /*...*/ private: MyClass<int> obj; }; /* OU - OU - OU */ #include <MyClass.h> class OtherClass : public MyClass<OtherClass> //par exemple { public: /*...*/ private: };Au pire, la connaissance des possibilités offertes par MyClass pour un type particulier ne devrait passer que par la connaissance de l'unité de compilation qui manipule effectivement ce type d'objet
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 /* pour ne fournir l'implémentation de MyClass<int> qu'à cette unité de * compilation */ #include <MyClass.tpp> /* implémentation des fonctions et des membres statiques de OtherClass */![]()
Merci koala51. Je t'applaudis, je ne sais pas comment tu fais pour trouver le temps et la patience de répondre autant dans les détails à chaque fois.![]()
Je retire ce que j'ai dit concernant les macros.
Mais le fait reste que si tu veux que certaines fonctions soient inlinées quand le compilateur le décide, il ne faut pas oublier les barrières qui l'empêchent. Si tu veux que l'optimisation traverse ces barrières, définir ces fonctions dans les headers (et donc, les déclarer static (sale!) ou inline (fait pour)) devient indispensable.
Ou encore mieux, bien concevoir son architecture de code.
C'est une problématique que je connais bien car le code qu'on a ici peut potentiellement facilement avoir ce souci (et je ne parle pas naturellement qu'en C++, mais aussi en Fortran où inline n'existe pas). On arrive toujours à restructurer son code correctement de sorte à ne plus avoir ce problème.
inline et extern/static c'est orthogonal.
Une fonction inline non static est tout de même extern, c'est juste qu'elle a un linkage "faible" (i.e. l'éditeur de liens se fiche d'avoir plusieurs définitions, il va considérer qu'elles sont toutes les mêmes)
Donc, tu soutiens qu'il est toujours possible d'avoir un projet où aucun composant ne propose à un autre composant de service qu'il serait plus approprié d'inliner (genre, un bête accesseur en lecture) ?
@loufoque: En théorie. Mais ça ne change rien au fait qu'un appel traversant une "frontière" (unité de compilation quand la LTCG est désactivée, bibliothèques autrement) ne sera pas inliné.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager