OVH Cloud OVH Cloud

inline, utilisation

21 réponses
Avatar
none
Quelles sont les motivations qui peuvent me pousser à utiliser des
méthodes inline ?
Par exemple, j'ai une classe avec des membres privés dont je déclare des
accesseurs (getFoo, setFoo)

public:
int getFoo() const;
void setFoo(int value);
private:
int m_foot;

Est-il judicieux de déclarer getFoo et setFoo en inline d'emblée, en
sachant que ce sont des fonctions au contenu trivial et petit (getFoo se
contente de renvoyer m_foot par exemple) ?

Merci pour vos conseils avisés

10 réponses

1 2 3
Avatar
kanze
Bertrand Motuelle wrote:
kanze wrote:
none (none) wrote:

Quelles sont les motivations qui peuvent me pousser à utiliser
des méthodes inline ?


Le profiler a dit que ça ne va pas autrement.


C'est effectivement le conseil que j'ai lu le plus souvent à
ce sujet. Mais quelles sont les indications concrètes que le
profiler peut donner ? Il ne me semble pas que quantify, par
exemple, puisse rapporter le coût de *l'appel* d'une fonction.
Doit-on se baser sur le nombre d'appels à la fonction ?


Tout dépend du profiler, mais en gros, si tu trouves trop de
temps dans toutes les fonctions qui utilisent ta fonction, ou
surtout dans une fonction qui l'utilise dans une boucle, c'est
un signe qu'il vaut la peine d'essayer.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
kanze
Patrick 'Zener' Brunet wrote:

Je réponds à none" <""(none)"@(none) <""(none)"@(none)">
Quelles sont les motivations qui peuvent me pousser à
utiliser des méthodes inline ? Par exemple, j'ai une classe
avec des membres privés dont je déclare des accesseurs
(getFoo, setFoo)

public:
int getFoo() const;
void setFoo(int value);
private:
int m_foot;

Est-il judicieux de déclarer getFoo et setFoo en inline
d'emblée, en sachant que ce sont des fonctions au contenu
trivial et petit (getFoo se contente de renvoyer m_foot par
exemple) ?


Je crois que ça dépend déjà du contexte:


Un peu, certainement.

* Si vous développez une librairie, il peut être intéressant
de proposer à votre utilisateur de choisir des fonctions
(normalement) plus efficaces sans lui imposer de relire le
code. Si vous êtes votre propre utilisateur, vous maîtrisez
mieux les usages.


Si tu vends des bibliothèques, tu as effectivement le problème
que tu n'as pas la possibilité d'effectuer du profiling sur les
applications finales, pour savoir exactement où ça coince. Et si
tu vends des bibliothèques de bas niveau, du genre bibliothèque
standard ou certains composents de Boost, tu peux être prèsque
sûr que parmi les clients, il y en aura un ou deux, au moins, où
la performance va être importante. Tu seras donc amené à faire
des optimisations (dont inline) sans avoir du feed-back d'un
profiler -- si j'implémentais la bibliothèque standard, par
exemple, std::vector<>::operator[] serait inline, dès le départ.

Mais les gens qui implémentent ce genre de bibliothèque sont
assez rares, et ces arguments ne valent que pour eux.

* Ensuite il y a plusieurs profils de programmeurs: certains
comptent sur le compilateur pour contrer leurs maladresses,
alors que d'autres s'efforcent de programmer à la fois fiable
et efficace. Parmi les seconds, certains y parviennent le plus
souvent et d'autres se trompent.


Ce n'est pas une question de « contrer les maladresses ». C'est
une question de laisser au compilateur faire ce qu'il sait faire
de mieux, et surtout d'investir son effort où il rapporte le
plus.

Il faut dire qu'en quinze ans de C++, en dehors des classes de
bases comme vector et list (ou dans l'époque pré-standard, mon
ArrayOf et DLListOf), je n'ai jamais rencontré un cas où inline
a eu un effet mesurable sur la performance. Au contraire, les
améliorations les plus importantes de la performance ont
toujours été simplifiées du fait qu'on ne mettait rien inline ;
pouvoir modifier l'algorithme et la structure de données d'a en
z sans avoir à récompiler tous les utilisateurs est un avantage
à ne pas negliger.

* Enfin il y a la qualité et "l'intelligence embarquée" du
compilateur: certains sont capables de restructurer
complètement le code, d'autres pas.

Donc AMHA, il n'y a pas de mal à mettre en place un maximum de
précision dans son code, pour autant qu'elle soit réfléchie,
et en sachant que le compilateur peut éventuellement la
remettre en question.

Donc typiquement pour une fonction qui ainsi emballe
simplement une ou deux instructions simples,
... faisant assez peu usage de la pile pour que son coût
d'invocation (en nombre d'instructions) soit important par
rapport au traitement lui-même,
... AMHA la déclaration inline est plutôt une amélioration.


Tu ne prends pas en compte son coût. Le fait qu'il rend tous les
clients dépendants de son implémentation. Dans la plupart des
cas, le coût est bien supérieur À n'importe quel avantage
imaginé.

Mais donc ça nécessite d'avoir une certaine connaissance du
protocole normal d'invocation sur le(s) système(s) visé(s).


C'est donc qu'en plus, ce n'est pas portable. (Je dirais qu'en
général, on écrit du code sans trop savoir sur quelles machines
il risque de tourner. Beaucoup de code que j'ai développé sur
Sparc tourne aujourd'hui sous Linux sur PC ; c'était une
évolution qu'on ne pouvait pas du tout prévoir quand je l'ai
écrit.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
loufoque
Quelles sont les motivations qui peuvent me pousser à utiliser des
méthodes inline ?


En méta-programmation par exemple, c'est conseillé.

Avatar
kanze
loufoque wrote:
Quelles sont les motivations qui peuvent me pousser à
utiliser des méthodes inline ?


En méta-programmation par exemple, c'est conseillé.


Pourquoi ? Dans beaucoup de cas de méta-programmation, la
fonction n'a même pas d'implémentation, puisqu'elle n'est jamais
appelée. (Elle ne sert que dans les sizeof, par exemple.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
loufoque

Pourquoi ? Dans beaucoup de cas de méta-programmation, la
fonction n'a même pas d'implémentation, puisqu'elle n'est jamais
appelée. (Elle ne sert que dans les sizeof, par exemple.)



Ben pour la factorielle par exemple.

template<int I> inline double Factorielle()
{
return Factorielle<I - 1>() * I;
}

template<> inline double Factorielle<0>()
{
return 1.0;
}

Factorielle<5>()

Avatar
Jean-Marc Bourguet
loufoque writes:


Pourquoi ? Dans beaucoup de cas de méta-programmation, la
fonction n'a même pas d'implémentation, puisqu'elle n'est jamais
appelée. (Elle ne sert que dans les sizeof, par exemple.)



Ben pour la factorielle par exemple.

template<int I> inline double Factorielle()
{
return Factorielle<I - 1>() * I;
}

template<> inline double Factorielle<0>()
{
return 1.0;
}

Factorielle<5>()


Ceci est un metaprogramme qui genere une serie de fonctions sans
argument retournant la valeur de la factorielle de leur argument
template. Il ne depend en rien de inline.

Ce n'est pas un metaprogramme qui retourne la factorielle de
l'argument template: le resultat n'est pas utilisable a la
compilation.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
loufoque

Ceci est un metaprogramme qui genere une serie de fonctions


Justement, non.
C'est le but de l'utilisation de inline dans ce cas.



Ce n'est pas un metaprogramme qui retourne la factorielle de
l'argument template: le resultat n'est pas utilisable a la
compilation.


Cela remplace Factorielle<5>() par 1.0*1*2*3*4*5

Avatar
loufoque

Ceci est un metaprogramme qui genere une serie de fonctions


Justement, non.
C'est le but de l'utilisation de inline dans ce cas.



Ce n'est pas un metaprogramme qui retourne la factorielle de
l'argument template: le resultat n'est pas utilisable a la
compilation.


Cela remplace Factorielle<5>() par 1.0*1*2*3*4*5



Si j'ai faux merci de me signaler la façon correcte de réaliser cela.


Avatar
Loïc Joly


Ceci est un metaprogramme qui genere une serie de fonctions



Justement, non.
C'est le but de l'utilisation de inline dans ce cas.



Ce n'est pas un metaprogramme qui retourne la factorielle de
l'argument template: le resultat n'est pas utilisable a la
compilation.



Cela remplace Factorielle<5>() par 1.0*1*2*3*4*5



Si j'ai faux merci de me signaler la façon correcte de réaliser cela.


Ce n'est pas faux dans le sens où ça calcule la valeur voulue.

C'est faux dans le sens où ça ne produit pas une constante à la
compilation, car il reste des appels de fonctions. On perd donc une
grande partie de l'intérêt de la chose.

La manière plus classique est (fautes de frappe à part) :

template <int i> class Fact
{
enum result {val = Fact<i-1>::val };
}

template<> class Fact<1>
{
enum result {val = 1};
}

int f()
{
int tableau[Fact<5>::val];
}


--
Loïc



Avatar
kanze
loufoque wrote:

Ceci est un metaprogramme qui genere une serie de fonctions


Justement, non.
C'est le but de l'utilisation de inline dans ce cas.

Ce n'est pas un metaprogramme qui retourne la factorielle de
l'argument template: le resultat n'est pas utilisable a la
compilation.


Cela remplace Factorielle<5>() par 1.0*1*2*3*4*5


Cela ne remplace pas Factorielle<5>. Il définit Factorielle<5>
comme :

inline double
Factorielle<5>()
{
return Factorielle<4>() * 5 ;
}

(ce qui provoque l'instantiation de Factorielle<4>).

En général, l'inline ne peut pas servir dans la
méta-programmation parce qu'il est optionnel. Un compilateur
n'est jamais obligé à le faire. Typiquement, d'ailleurs, un
compilateur a des limites (parfois assez basses) à la profondeur
d'imbrication des fonctions qu'il génère en ligne, ce qui veut
dire que pour un i suffisamment grand, Factorielle<i> ne serait
pas générée complétement en ligne.

Pour être la méta-programmation, ce genre de manipulation doit
se baser sur des « integral constant-expression » définies dans
§5.19/1. Quelque chose du genre :

template< unsigned long i >
struct Factorielle
{
static unsigned long const value
= i * Factorielle< i - 1 >::value ;
operator unsigned long() const
{
return value ;
}
} ;

template<>
struct Factorielle< 0UL >
{
static unsigned long const value = 1UL ;
operator unsigned long() const
{
return value ;
}
} ;

Une expression comme Factorielle<5>() devient donc une
invocation du constructeur d'un objet de type Factorielle<5>,
qui contient une constante qui vaut 120, et qui se convertit
implicitement en un unsigned long avec cette valeur.

(Mais note bien une subtilité dans son utilisation : dans
unsigned long const f5a = Factorielle<5>() ;
f5a n'est pas une « expression constante ». Pour avoir une
expression constante, il faut faire :
unsigned long const f5b = Factorielle<5>::value ;
Et :
#define NULL (Factorielle<5>::value - 120)
est une définition légale (mais tordu) de NULL:-).)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


1 2 3