 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Vincent Richard Guest
|
Posted: Wed Sep 03, 2003 7:42 pm Post subject: Eviter le coût de la résolution de "virtual" |
|
|
Bonsoir,
Soit le programme suivant :
class A
{
public:
virtual void f(const int p) = 0;
};
class B : public A
{
public:
void f(const int p) { /* ... */ }
};
int main()
{
A* a = new B;
for (int i = 0 ; i < 10000 ; ++i)
a->f(i);
}
Je crois savoir qu'à chaque fois que l'on fait un appel à 'f()', une
"recherche" est effectuée pour appeler la fonction correcte selon
l'objet sur lequel on l'appelle. Cette recherche a un coût.
Y'a-t-il un moyen de la limiter, par exemple, en obtenant l'adresse
une première fois (résolution), puis en l'appelant ensuite (simple
appel, sans résolution), ce qui fait que la résolution n'est pas
faite à chaque itération ?
Par exemple :
int main()
{
A* a = new B;
PointeurSurLaFonction pf = ???;
for (int i = 0 ; i < 10000 ; ++i)
(pf)(i);
}
Apparemment, cela ne peut pas être résolu avec les pointeurs sur
fonctions membres :
int main()
{
A* a = new B;
typedef void (A::*FuncPtr)(int);
FuncPtr pf = &A::f;
for (int i = 0 ; i < 10000 ; ++i)
(a->*pf)(i);
}
....puisque dans ce cas, la résolution est évidemment quand même faite.
Je ne sais pas si je suis très explicite...
Merci d'avance pour vos réponses.
Vincent
--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
|
|
| Back to top |
|
 |
Fabien LE LEZ Guest
|
Posted: Wed Sep 03, 2003 8:27 pm Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
On Wed, 03 Sep 2003 21:42:50 +0200, Vincent Richard
<chere-loque.MARRE-DE-LA-PUB (AT) wanadoo (DOT) fr.invalid> wrote:
| Quote: | Cette recherche a un coût.
|
Cette recherche a-t-elle un coût suffisant pour ralentir ton
application de façon significative ?
|
|
| Back to top |
|
 |
Vincent Richard Guest
|
Posted: Wed Sep 03, 2003 8:36 pm Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
Le Mercredi 3 Septembre 2003 22:27, Fabien LE LEZ a écrit :
| Quote: | On Wed, 03 Sep 2003 21:42:50 +0200, Vincent Richard
[email]chere-loque.MARRE-DE-LA-PUB (AT) wanadoo (DOT) fr.inva[/email]lid> wrote:
Cette recherche a un coût.
Cette recherche a-t-elle un coût suffisant pour ralentir ton
application de façon significative ?
|
Cette fonction est appelée dans le cadre du tri d'une liste
de plusieurs dizaines de milliers d'éléments, donc je pense que
oui, c'est significatif.
Bon, et puis c'est aussi à titre d'information, pour ma culture
personnelle ! :-)
Vincent
--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
|
|
| Back to top |
|
 |
Fabien LE LEZ Guest
|
Posted: Wed Sep 03, 2003 8:45 pm Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
On Wed, 03 Sep 2003 22:36:12 +0200, Vincent Richard
<chere-loque.MARRE-DE-LA-PUB (AT) wanadoo (DOT) fr.invalid> wrote:
| Quote: | Cette recherche a-t-elle un coût suffisant pour ralentir ton
application de façon significative ?
Cette fonction est appelée dans le cadre du tri d'une liste
de plusieurs dizaines de milliers d'éléments, donc je pense que
oui, c'est significatif.
|
Tu ne peux pas le savoir sans faire de mesures du temps d'exécution.
On a souvent des surprises, surtout avec la capacité d'optimisation
des compilateurs et les architectures tordues des processeurs.
|
|
| Back to top |
|
 |
Durand Richard Guest
|
Posted: Wed Sep 03, 2003 9:04 pm Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
As-tu fait des tests de temps d'exécution avec une fonction membre non
virtuel ? Car il me semble que ce genre d'optimisation soit à la portée d'un
compilateur moderne.
|
|
| Back to top |
|
 |
Frédéri MIAILLE Guest
|
Posted: Wed Sep 03, 2003 9:14 pm Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
| Quote: | int main()
{
A* a = new B;
typedef void (A::*FuncPtr)(int);
FuncPtr pf = &A::f;
for (int i = 0 ; i < 10000 ; ++i)
(a->*pf)(i);
}
|
A mon avis, ce n'est pas possible et la recherche est obligatoire car ce
pointeur peut changer de type d'une itération à l'autre et dans cette
hypothèse la recherche est tout de même effectuée. Même s'il n'en est rien.
Première hypothèse :
Ce qui se produirait, et conformément à ce que tu disais, logiquement, c'est
que A::f() soit resolu lors de son appel ou encore, que pf devienne fou
entre
FuncPtr pf = &A::f; et f() qui changerai alors d'adresse.
Donc, comment veux-tu retenir l'adresse de quelque chose qui va changer lors
de son emploi ?
Seconde hypothèse :
A la rigueur, tu vas retenir un pointeur sur fonction qui va lorsque tu vas
l'utiliser, effectuer également le même type de recherche.
Et je ne sais pas si un pointeur sur fonction virtuelle pure est prévu dans
l'emploi du langage.
Troisième et dernière hypothèse :
Et puis, à ce moment là, ta fonction virtuelle ne sert à rien. Autant faire
un objet du type dérivé sans passer par la classe de base, donc redéfinir
ton objet, pour ensuite l'employer tranquillement, puisqu'il ne change pas.
--
Frédéri MIAILLE
fr.comp.lang.c
fr.comp.lang.c++
fr.comp.graphisme.programmation
fr.comp.os.ms-windows.programmation
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Thu Sep 04, 2003 8:11 am Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
Vincent Richard <chere-loque.MARRE-DE-LA-PUB (AT) wanadoo (DOT) fr.invalid> wrote
in message news:<3f56452c$0$27055$626a54ce (AT) news (DOT) free.fr>...
| Quote: | Soit le programme suivant :
class A
{
public:
virtual void f(const int p) = 0;
};
class B : public A
{
public:
void f(const int p) { /* ... */ }
};
int main()
{
A* a = new B;
for (int i = 0 ; i < 10000 ; ++i)
a->f(i);
}
Je crois savoir qu'à chaque fois que l'on fait un appel à 'f()', une
"recherche" est effectuée pour appeler la fonction correcte selon
l'objet sur lequel on l'appelle. Cette recherche a un coût.
|
Un coût très faible, quand même. Il y a des cas où ça importe, mais ils
ne sont pas si fréquent que ça.
| Quote: | Y'a-t-il un moyen de la limiter, par exemple, en obtenant l'adresse
une première fois (résolution), puis en l'appelant ensuite (simple
appel, sans résolution), ce qui fait que la résolution n'est pas faite
à chaque itération ?
|
Utiliser un compilateur avec un bon optimisateur, un qui comprend le
C++. L'optimisation en question, d'enlever des calculs constants d'une
boucle, est une des plus classiques. Le seul problème pour le
compilateur, c'est de reconnaître que le calcul est constant, c-à-d en
fait, que le vptr de l'objet est une constante la vie durante de
l'objet. Si l'optimisateur connaît le C++, pas de problème. Sinon, c'est
quasiment impossible, puisqu'un pointeur à l'objet est passé à f, qui
pourrait en théorie bien modifier ce pointeur.
| Quote: | Par exemple :
int main()
{
A* a = new B;
PointeurSurLaFonction pf = ???;
for (int i = 0 ; i < 10000 ; ++i)
(pf)(i);
}
Apparemment, cela ne peut pas être résolu avec les pointeurs sur
fonctions membres :
|
Certainement, mais le resultat risque fort d'être moins rapide qu'un
appel à une fonction virtuelle.
| Quote: | int main()
{
A* a = new B;
typedef void (A::*FuncPtr)(int);
FuncPtr pf = &A::f;
for (int i = 0 ; i < 10000 ; ++i)
(a->*pf)(i);
}
...puisque dans ce cas, la résolution est évidemment quand même faite.
|
| Quote: | Je ne sais pas si je suis très explicite...
|
Pourquoi pas a->B::f(i) ? Si tu sais le type réel au moment que tu écris
la boucle. (Mais à ne faire que si le profiling le montre réelement
nécessaire. C'est une piège pour la maintenance.)
--
James Kanze GABI Software mailto:kanze (AT) gabi-soft (DOT) fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Thu Sep 04, 2003 8:27 am Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
"Frédéri MIAILLE" <bobranger (AT) wanadoo (DOT) fr> wrote
| Quote: | int main()
{
A* a = new B;
typedef void (A::*FuncPtr)(int);
FuncPtr pf = &A::f;
for (int i = 0 ; i < 10000 ; ++i)
(a->*pf)(i);
}
A mon avis, ce n'est pas possible et la recherche est obligatoire car
ce pointeur peut changer de type d'une itération à l'autre et dans
cette hypothèse la recherche est tout de même effectuée. Même s'il
n'en est rien.
|
Et comment le pointeur peut-il changer de type d'une itération à
l'autre ? Il ne change pas de valeur dans la boucle ici, et il n'est
accessible nul part ailleur. La seule possibilité, c'est que si la
fonction f fait quelque chose du genre :
this->~B() ;
new ( this ) C() ;
(où C est aussi dérivé de A) ; je ne me mettrais pas ma main au feu,
mais je crois que ça invoque un comportement indéfini (du fait que la
position de la partie A dans C n'est pas garantie être la même que la
position de la partie A dans B, si rien d'autre).
Mais qu'il utilise un pointeur à une fonction membre, ou qu'il fait
l'appel direct, ne change en principe rien, et j'imagine que l'analyse
dans le cas de l'appel direct serait plus simple pour le compilateur.
| Quote: | Première hypothèse :
Ce qui se produirait, et conformément à ce que tu disais, logiquement,
c'est que A::f() soit resolu lors de son appel ou encore, que pf
devienne fou entre FuncPtr pf = &A::f; et f() qui changerai alors
d'adresse. Donc, comment veux-tu retenir l'adresse de quelque chose
qui va changer lors de son emploi ?
|
C'est le problème du compilateur, et c'est la raison pourquoi les
pointeur à des fonctions membres sont souvent assez gros -- typiquement,
il contient l'information si la fonction est virtuelle ou non, et soit
l'adresse réele de la fonction non-virtuelle, soit la position de
l'entrée dans la vtable de la fonction virtuelle.
| Quote: | Seconde hypothèse :
A la rigueur, tu vas retenir un pointeur sur fonction qui va lorsque
tu vas l'utiliser, effectuer également le même type de recherche.
|
C'est une possibilité. Ça s'appelle une trampoline, et je sais que g++
s'en servais dans certains cas, au moins dans la passée. Je ne sais pas
si les pointeurs à des fonctions membres en étaient un des cas.
| Quote: | Et je ne sais pas si un pointeur sur fonction virtuelle pure est prévu
dans l'emploi du langage.
|
Je m'en sers tous le temps.
| Quote: | Troisième et dernière hypothèse :
Et puis, à ce moment là, ta fonction virtuelle ne sert à rien. Autant
faire un objet du type dérivé sans passer par la classe de base, donc
redéfinir ton objet, pour ensuite l'employer tranquillement, puisqu'il
ne change pas.
|
Il sait que dans la boucle en question, la virtualité ne sert à rien. Je
suppose qu'il l'a introduit pour d'autres parties de l'application,
qu'il ne nous a pas montrées.
--
James Kanze GABI Software mailto:kanze (AT) gabi-soft (DOT) fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16
|
|
| Back to top |
|
 |
Michaël Cortex Guest
|
Posted: Thu Sep 04, 2003 8:35 am Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
Vincent Richard wrote:
| Quote: | Je crois savoir qu'à chaque fois que l'on fait un appel à 'f()', une
"recherche" est effectuée pour appeler la fonction correcte selon
l'objet sur lequel on l'appelle. Cette recherche a un coût.
|
Ce n'est *vraiment* une recherche (je ne sais pas dans quel sens tu
l'entends). La fonction est prise dans la vtable de l'objet pointé. Du coup,
on ne fait de truc du style "regarder de quel type est l'objet, puis appeler
la bonne fonction" (qui pourrait être en effet lent). Mais on fait plutôt
"trouve sa vtable et appelle la fonction 'f' dedans", et la vtable change
pour chaque type. C'est un peu le principe du polymorphisme (mais à une
autre échelle).
Stroustup, dans TCPL, dit que ce "overhead" devrait être ignoré, car presque
supprimé par les bons compilateurs... Mais bon, c'est de la théorie Ca
n'a jamais été critique à un ce point pour moi que je voulais optimiser sur
ce point.
--
<=- Michaël "Cortex" Monerau -=>
|
|
| Back to top |
|
 |
Vincent Richard Guest
|
Posted: Thu Sep 04, 2003 9:14 am Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
Merci à tous pour vos réponses, je vais donc laisser comme cela et
laisser faire le compilateur.
Vincent
--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
|
|
| Back to top |
|
 |
Christophe Lephay Guest
|
Posted: Thu Sep 04, 2003 10:39 am Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
<kanze (AT) gabi-soft (DOT) fr> a écrit dans le message de
news:d6652001.0309040011.2ca57bc8 (AT) posting (DOT) google.com...
| Quote: | Vincent Richard <chere-loque.MARRE-DE-LA-PUB (AT) wanadoo (DOT) fr.invalid> wrote
in message news:<3f56452c$0$27055$626a54ce (AT) news (DOT) free.fr>...
int main()
{
A* a = new B;
for (int i = 0 ; i < 10000 ; ++i)
a->f(i);
}
Pourquoi pas a->B::f(i) ? Si tu sais le type réel au moment que tu écris
la boucle.
|
Je crois que ce qu'il avait en tête, c'est de ne pas connaitre le type de a
à la compilation...
Chris
|
|
| Back to top |
|
 |
Samuel Krempp Guest
|
Posted: Thu Sep 04, 2003 10:52 am Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
le Jeudi 4 Septembre 2003 10:11, [email]kanze (AT) gabi-soft (DOT) fr[/email] écrivit :
| Quote: | Vincent Richard <chere-loque.MARRE-DE-LA-PUB (AT) wanadoo (DOT) fr.invalid> wrote
in message news:<3f56452c$0$27055$626a54ce (AT) news (DOT) free.fr>...
Je crois savoir qu'à chaque fois que l'on fait un appel à 'f()', une
"recherche" est effectuée pour appeler la fonction correcte selon
l'objet sur lequel on l'appelle. Cette recherche a un coût.
Un coût très faible, quand même. Il y a des cas où ça importe, mais ils
ne sont pas si fréquent que ça.
|
je sais pas exactement ce que va faire Richard, mais ça pourrait ressembler
à un prog de calcul, et en tout cas, pour des progs de calcul, le coût du
virtual importe énormément si l'appel a lieu au sein de l'itération de la
boucle la plus interne. ('inner-most loop' ..)
Aucune lib de calcul ne mettrait de fonctions virtuelles qui pourrait se
retrouver dans cette situation..
un exemple de temps d'éxécution virtual / non-virtual dans une boucle de 10
millions d'itérations (source attaché), donne :
non-virtual. Time=0.05
virtual. Time=0.15
ratio : 3
avec g++-3.3.2 (-O3 ), sur Pentium 4.
on peut s'y attendre : sans virtual, l'operator() est inliné et l'itération
s'éxécute 3 fois plus vite.
L'exemple n'est pas absurde, ça serait parfois utile d'avoir du
polymorphisme sur des objets du genre 'Iterated_Operation'.
mais vu l'imapct de 'virtual' dans cette situation (et l'importance de
l'inlining), les libs de calcul obtiennent du polymorphisme par templates,
pas par des fonctions virtuelles.
--
Sam
Enlever les mots en trop dans mon e-mail pour répondre
|
|
| Back to top |
|
 |
Samuel Krempp Guest
|
Posted: Thu Sep 04, 2003 12:59 pm Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
le Jeudi 4 Septembre 2003 12:52, [email]krempp (AT) crans (DOT) trucs.en.trop.org[/email] écrivit :
| Quote: | un exemple de temps d'éxécution virtual / non-virtual dans une boucle de
10 millions d'itérations (source attaché), donne :
non-virtual. Time=0.05
virtual. Time=0.15
ratio : 3
avec g++-3.3.2 (-O3 ), sur Pentium 4.
|
J'ai un peu compliqué (utilisé une Iterated_Operation * externe pour tenter
de forcer le comportement virtuel, et distingué plusieurs types de boucles,
for_each, explicite, ..)
Avec intel-7.1 linux, j'obtiens à peu près les mêmes chronos (mais les cas
où il arrive à optimiser la boucle non-virtuelle ne sont pas les mêmes. il
y arrive avec une boucle explicite (avec un compteur entier, mais pas avec
une boucle explicite sur un pointeur), mais pas avec for_each - tandis que
c'est l'inverse pour g++.
en résumé (et pour les 2 compilos), j'obtiens en gros :
0.05s pour les boucles non-virtuelles optimisées
0.15s pour les boucles non-virtuelle quand l'optimisation n'a pas lieu
(for_each pour intel, boucle explicite pour g++)
0.19s pour la boucle virtuelle.
Les temps varient un peu d'un compilo un autre, et d'une éxécution à l'autre
(d'un chouillat), mais les rapports restent à chaque fois consistant :
x3 entre boucle non-virtuelle selon qu'elle est optimisée ou non.
(intel optimisant la boucle explicite, g++ optimisant le for_each)
Quelqu'un a une idée du genre d'optimisation dont il peut s'agir, et
pourquoi ce n'est activé que dans un type de boucle ?
Je pensais que les 2 types de boucles donneraient lieux aux mêmes types
d'optimisation (inliner le code), mais du coup je m'interroge.
+20% entre la boucle non-virtuelle la plus lente et la boucle virtuelle.
Est-ce qu'on peut raisonnablement considérer que le cout de l'appel virtuel
a un surcoût de 20% par rapport au coût de la boucle elle-même + l'appel de
fonction basique ?
--
Sam
Enlever les mots en trop dans mon e-mail pour répondre
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Thu Sep 04, 2003 3:16 pm Post subject: Re: Eviter le coût de la résolution de "virtual" |
|
|
Samuel Krempp <krempp (AT) crans (DOT) trucs.en.trop.org> wrote
| Quote: | le Jeudi 4 Septembre 2003 10:11, [email]kanze (AT) gabi-soft (DOT) fr[/email] écrivit :
Vincent Richard <chere-loque.MARRE-DE-LA-PUB (AT) wanadoo (DOT) fr.invalid> wrote
in message news:<3f56452c$0$27055$626a54ce (AT) news (DOT) free.fr>...
Je crois savoir qu'à chaque fois que l'on fait un appel à 'f()',
une "recherche" est effectuée pour appeler la fonction correcte
selon l'objet sur lequel on l'appelle. Cette recherche a un coût.
Un coût très faible, quand même. Il y a des cas où ça importe, mais
ils ne sont pas si fréquent que ça.
je sais pas exactement ce que va faire Richard, mais ça pourrait
ressembler à un prog de calcul, et en tout cas, pour des progs de
calcul, le coût du virtual importe énormément si l'appel a lieu au
sein de l'itération de la boucle la plus interne. ('inner-most loop'
..)
|
C'est ce que j'ai dit, non ? Il y a des cas où ça importe. Ils ne sont
pas si fréquents que ça, mais ils existent bien.
En fait, le coût effectif dépend de beaucoup de chose : le processeur,
le compilateur, et ce qu'on fait dans la fonction. Je sais que le coût
(rélatif) est beaucoup plus grand sur des processeurs modernes, par
exemple, qu'il ne l'a été. Et qu'évidemment, si le processeur réussi à
mettre la fonction non-virtuelle inline, ça expose des possibilités
supplémentaire d'optimisation qui pourrait être non-négligible. Je sais
aussi qu'il y a de grandes différences entre les compilateurs en ce qui
concerne la qualité de l'optimisation des fonctions virtuelles.
| Quote: | Aucune lib de calcul ne mettrait de fonctions virtuelles qui pourrait
se retrouver dans cette situation..
un exemple de temps d'éxécution virtual / non-virtual dans une boucle
de 10 millions d'itérations (source attaché), donne :
non-virtual. Time=0.05
virtual. Time=0.15
ratio : 3
avec g++-3.3.2 (-O3 ), sur Pentium 4.
on peut s'y attendre : sans virtual, l'operator() est inliné et
l'itération s'éxécute 3 fois plus vite.
|
Sur ma machine de travail, la différence en coût de l'appel d'une
fonction vide, non-inline, et de l'ordre de 10. C-à-d que ça peut bien
faire une différence. Si la fonction appelée est courte, évidemment, et
si le compilateur n'arrive pas à résoudre le type statiquement. En
revanche, le coût absolu est extrèmement faible dans les deux cas. C-à-d
que dès qu'il y a du traitement dans la fonction, la différence devient
negligible : qu'importe qu'une partie du traitement va 10 fois plus
vite, si cette partie ne représente 1% du temps total.
--
James Kanze GABI Software mailto:kanze (AT) gabi-soft (DOT) fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16
|
|
| Back to top |
|
 |
Gabriel Dos Reis Guest
|
Posted: Thu Sep 04, 2003 3:22 pm Post subject: Re: Eviter le coût de larésolution de "virtual" |
|
|
Laurent Deniau <Laurent.Deniau (AT) cern (DOT) ch> writes:
| Quote: | Negligeable. C'est du meme ordre de rapidite qu'un appel de fonction
standard depuis l'introduction des chunks (orthographe
^ |
s/c/t/
[...]
| Quote: | Pour plus d'info, lire Design and Development of C++ de BS.
^^^^^^^^^^^ |
s/Development/Evolution/
-- Gaby
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|