OVH Cloud OVH Cloud

vitesse: if vs pointeur de methode

126 réponses
Avatar
Guillaume Desticourt
bonsoir,

je m interroge sur la vitesse d execution entre une comparaison et un
pointeur de methode.
j ai une class dont une methode peut changer de comportement au cours de
la vie du process, mais cela rarement.
je me demandais si je devais avoir une methode unique avec un bloc
if/else ou alors un pointeur de methode sette a la methode qui va bien.
j ai donc ecrit deux petits programmes de test, et la solution if/else
est /visiblement/ plus rapide.
et donc je me demandais:
- est ce que mon test est pertinent?
- pourquoi une telle difference de temps?

les prog ont ete compile sous un linux 2.6 avec g++ 3.3.5

merci,

def.hh
------


#ifndef DEF_HH
# define DEF_HH

#include <stdlib.h>

#define MAX_LOOP 1000000000

class Test;

typedef int (Test::*behavior_t)(void);

class Test
{
public:
Test() :
_test(true)
{
}

int behavior1(void)
{
return 0;
}
int behavior2()
{
return 0;
}

inline bool isTrue(void)
{
return _test;
}
private:
bool _test;
};

#endif

if.cc
-----


#include "def.hh"

int main(void)
{
Test * test = new Test();

for (unsigned long u = 0;
u < MAX_LOOP;
++u)
{
if (test->isTrue())
test->behavior1();
else
abort();
}
return 0;
}


pointer.cc
----------



#include "def.hh"

int main(void)
{
Test * test = new Test();
behavior_t behavior;
if (test->isTrue())
behavior = &Test::behavior1;
else
abort();

(test->*behavior)();


for (unsigned long u = 0;
u < MAX_LOOP;
++u)
{
(test->*behavior)();
}
return 0;
}


Makefile
--------

all: iftest pointertest

iftest: def.hh
g++ -Wall -Werror -O2 if.cc -o iftest

pointertest: def.hh
g++ -Wall -Werror -O2 pointer.cc -o pointertest

clean:
rm -f *.o
rm -f *~
rm -f iftest pointertest


une tarball est - provisoirement - disponible ici:
http://www.freenopen.net/~guillaume/info/prog/source/ifpointerbench-20050720-1755.tar.bz2

--
Guillaume Desticourt

10 réponses

Avatar
kanze
Guillaume Desticourt wrote:
wrote:
Guillaume Desticourt wrote:



On Wed, 20 Jul 2005 18:30:57 +0200, Guillaume Desticourt
:

- est ce que mon test est pertinent?


Est-ce que la différence sera réellement visible par
l'utilisateur, dans le programme final ?


je ne sais pas. la en faisant un boucle de 1000000000
iterations, la difference est visible a l oeil nu.
maintenant je n ai aucune idee du volume de donnees que mon
programme aura a traiter.


Qu'importe, d'un certain côté.
[...]

Le choix ne peut influer sur la vitesse totale d'exécution
que si cette partie représente une partie significative du
temps d'exécution.


je sais tout de meme que cette partie du code sera appelee
extremement souvent, donc faire des economies a ce niveau a
tout de meme des chances d'etre rentable.


Comme ça a des chances de ne pas l'être. C'est un fait établi
que même les meilleurs programmeurs ne sont pas capables de dire
en avance où va se trouver le goulot d'étranglement. Ou souvent,
même s'il y en aurait un. (Il y a évidemment quelques exceptions
en ce qui concerne les bibliothèques vraiement de base.)

Chose que tu ne sauras de toute façon qu'une fois le
programme complet, avec les données du profiler. Tout essai
à faire quelque chose de ce genre avant est contreproductif.


donc tu preconises de ne pas s occuper de performance avant
d'avoir code le programme - comme Fabien Lelez si je vous ai
bien suivi...


Tout à fait. Vue que s'en occuper avant n'a que des
désavantages.

[...]

Code de la manière la plus claire et lisible possible, et
occupe-toi des problèmes de performances si le programme
final est effectivement trop lent.


heu bof, je trouve plus logique de viser le resultat desire
plutot qu'appliquer des rustines et me retrouver avec un
code - encore plus - illisible.


Je vois que tu n'as aucune expérience avec des vrais
programmes.


j'en ai ecrits pas mal de faux alors...


Je ne sais pas ce que tu as écrit, mais si tu penses que
d'écrire du code bien propre et encapsuler, et puis de
n'optimiser qu'où il le faut, donne du code moins lisible que
d'optimiser partout, au cas où, dès le départ, et sans s'occuper
de la lisibilité, il y a bien un problème quelque part.

[...]
Si d'autres ne peut pas comprendre ton code, c'est foutu
d'avance. Ce que Fabien disait, justement, c'est d'écrire du
code que les autres peuvent comprendre.


bof. des beaux principes, mais comprendre le code de quelqu'un
d'autre est amha une des choses les plus diffiles.


Ça dépend qui l'a écrit. Je sais que j'ai souvent eu à
comprendre le code écrit par des autres. Parfois, c'est
prèsqu'impossible. Mais il m'est arrivé des fois où c'était même
facile.

Curieusement (on non), ce sont les cas où il était le plus
facile qui était aussi les programmes avec le moins d'erreurs.

Aussi, les cas du code illisible appartient largement au passée.
Aujourd'hui, je dirais que les revues de code ont fait leur
preuves. Et un code illisible ne passe pas une revue de code.

Des qu'un programme commence a avoir une certaine
taille/complexite, cela devient ardu, malgre la lisibilite du
code, les patrons de conception etc...


Il y a des niveaux. On ne régarde jamais tout le programme à la
fois, au même niveau de détail.

--
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
Pierre Maurette wrote:
On Wed, 20 Jul 2005 18:30:57 +0200, Guillaume Desticourt
:

- est ce que mon test est pertinent?


Est-ce que la différence sera réellement visible par
l'utilisateur, dans le programme final ?

Code de la manière la plus claire et lisible possible, et
occupe-toi des problèmes de performances si le programme
final est effectivement trop lent.


Je trouve votre réponse étonnante.


Pas dans la contexte. Dans la contexte, c'est même la seule
réponse qui convient.

Vous considérez à priori que toute question sur l'optimisation
ne sera pas posée.


Dans la contexte, il ne parlait pas de « toute question sur
l'optimisation ». Il parlait d'une micro-optimisation au niveau
du codage. En gros, avant le profiling, les seules optimisations
raisonables sont des optimisations algorithmiques : si pour une
raison quelconque, on ne peut pas se servir de std::sort, et
qu'on sait qu'on va avoir une million d'éléments à trier, on
écrit tout de suite une quick sort, et non un tri par insertion.

Existent, pratiquant le C++, des gens qui ne sont pas plus
idiots que vous et qui ont le droit de (se) poser des
questions, éventuellement pour pour le plaisir, ou ne pas
mourir idiots. Vous avez celui, si vous ne savez pas, de ne
pas répondre.


Pardon, mais Fabien n'a fait que répéter une injonction bien
connue. Le poster original a bien fait comprendre que ce n'était
pas une curiosité intellectuelle, mais une question sérieuse sur
un projet réel. Fabien a, en fait, donné le seul conseil
acceptable dans une contexte professionnelle.

[...]
Quand au ton que vous employez, l'impératif, le tutoiement, il
n'est pas simplement grossier, il est grotesque.


Le tutoiement est d'usage dans les forums. L'impératif convient
quand on dit à quelque ce qu'il doit faire. Je ne vois pas de
problème. On pourrait formuler différemment : « il serait
éventuellement mieux de », ou « il n'y a qu'un con pour ne
pas ». (Deux formulations qui évite le tutoiement, d'ailleurs.)
L'impératif n'est qu'un point quelque part entre ces deux
extrèmes, et je ne crois pas que Fabien le voulait offensif.

--
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
Loïc Joly wrote:
oui enfin dans le cas present je ne vois pas trop le
rapport, l'encapsulation est la que je fasse

if
test->behavior1()
else
test->behavior2()

ou
(test->*behavior)();


Sauf que ce que James proposais, c'était d'utiliser le pattern
strategy, qui apporte certainement une souplesse
supplémentaire (et qui a plus de chance d'être optimisé que
l'appel par pointeur de fonction membre, en plus).


À vrai dire, je ne parlais pas de ce cas précis, en tant que tel
(même s'il me semble bien que le modèle de stratégie soit la
solution « consacrée »). Sans savoir plus sur la contexte, je
ne peux pas dire laquelle des solutions convient le plus. Mais
mon choix serait sûrement basé sur les critères de conception,
par exemple, où je voulais rendre le choix explicit, et non sur
des considérations de micro-optimisation.

--
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
Fabien LE LEZ wrote:
On Thu, 21 Jul 2005 22:11:08 +0200, "Pierre Maurette"
:

Vous considérez à priori que toute
question sur l'optimisation ne sera pas posée.


L'OP avait posé (entre autre) une question : "est ce que mon
test est pertinent?"

J'ai répondu : non. (Bon, d'accord, je ne l'ai pas dit
explicitement. Alors je le fais maintenant pour celui qui
n'avait pas compris.)


Je crois, en fait, qu'il a démandé plutôt si ses essais de
mesure mesuraient ce qu'il voulait (dont la réponse est non).
Mais tu as bien fait de signaler que les mesures étaient sans
signification réele pour sont application, et que la question
n'était pas pertinante pour écrire le code.

--
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
Michel Michaud
Dans le message ,
"Michel Michaud" writes:
Quelqu'un a-t-il déjà vu une explication du principe des
« méthodes » où ce terme semble justifié adéquatement ?


Si un gugus s'amène sur ce forum et s'entête à ne converser
exclusivement qu'en Anglais (tout en sachant parler Français et le
status de ce forum), tu trouverais cela un brin déplacé, non ?


Je ne suis pas sûr de comprendre ta réponse, mais je voulais
parler du cas général. Par exemple dans un livre sur Smalltalk,
Java, C# ou autre langage qui utilise ce terme. Quand on présente
le concept de fonction dans un langage de programmation comme C,
il est facile d'expliquer le terme en faisant la relation avec une
fonction mathématique (moins évident en C et dérivées depuis qu'on
a les fonctions void, mais encore là, on peut donner l'explication
historique au moins). Si j'avais à écrire un livre sur Java ou C#,
j'aimerais présenter tout aussi logiquement le terme méthode et
je n'ai jamais rien vu de bien fort à ce sujet. Il faut dire que
je n'ai aucun livre spécifique sur Smalltalk (où le terme a été
inventé je crois).

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
Fabien LE LEZ
On Thu, 21 Jul 2005 22:11:08 +0200, "Pierre Maurette"
:

Quand au ton que vous employez, l'impératif, le tutoiement, il n'est
pas simplement grossier, il est grotesque.


Oups, je n'avais pas vu ça. C'est donc un troll. Plonk.

Avatar
Richard Delorme

Quels sont les autres reproches à faire à Java pour le
développement de gros projets ?


Le plus gros problème, évidemment, c'est qu'il melange
implémentation et interface (au sens général, et non au sens du
mot clé Java). Le langage même (et non seulement les
implémentations actuelles) exige que je mets tous les détails de
l'implémentation dans le même fichier physique que la
spécification de l'interface, et même que je les melange à
l'intérieur du fichier -- je ne peux pas, par exemple, mettre
la spécification en tête du fichier, et l'implémentation à la
suite. Alors que dans tous les gros projets où j'ai travaillé en
C++, il fallait davantage d'autorité pour modifier un en-tête
(dont dépendent les autres) qu'un fichier source.
[...]


Je ne suis pas trop d'accord sur ce point. En Java, l'interface s'écrit
à l'aide des commentaires et de javadoc. Le résultat est beaucoup plus
clair qu'un en-tête C++, et permet des choses assez fines comme le
marquage "deprecated" des membres qu'ils ne faut plus utiliser. Quant à
ne plus modifier une interface, ce n'est qu'un problème de discipline,
et rien n'empêche la discipline en Java ou l'indiscipline en C++.


Pour la reste, j'ai des assez bonnes expériences avec la
programmation par contrat dans de grands projets. Ici, le C++
n'a pas de supporte direct, mais il s'est développé des idiomes
(avec des fonctions virtuelles privées) pour l'implémenter,
quand elle convient. Ici, le langage de Java rend la
programmation par contrat même impossible, ou au moins, beaucoup
plus difficile : il faut se limiter à l'héritage simple, avec
beaucoup de classes supplémentaires intermédiaires. De même, je
préfère les erreurs le plus tôt possible -- l'absense des
templates et le fait que tout dérive d'Object pose un problème
ici (mais je crois que ce problème est reconnu même par ceux qui
font le Java, et que des versions les plus récentes de Java ont
des templates).


Là je serais plutôt d'accord sur ces points.

--
Richard


Avatar
James Kanze
Richard Delorme wrote:

Quels sont les autres reproches à faire à Java pour le
développement de gros projets ?




Le plus gros problème, évidemment, c'est qu'il melange
implémentation et interface (au sens général, et non au sens
du mot clé Java). Le langage même (et non seulement les
implémentations actuelles) exige que je mets tous les détails
de l'implémentation dans le même fichier physique que la
spécification de l'interface, et même que je les melange à
l'intérieur du fichier -- je ne peux pas, par exemple, mettre
la spécification en tête du fichier, et l'implémentation à la
suite. Alors que dans tous les gros projets où j'ai travaillé
en C++, il fallait davantage d'autorité pour modifier un
en-tête (dont dépendent les autres) qu'un fichier source.



[...]


Je ne suis pas trop d'accord sur ce point. En Java,
l'interface s'écrit à l'aide des commentaires et de javadoc.


I'interface se documente à l'aide des commentaires et de
javadoc. L'interface qui voit le compilateur, c'est bien celle
qui est éparpillée dans l'implémentation. D'ailleurs, à
l'encontre de Doxygen, javadoc exige (ou au moins exigeait --
mes connaissances de Java ne sont pas des plus récentes) que la
documentation se trouve aussi éparpillé à travers
l'implémentation.

Or que dans un grand projet, normalement, la spécification de
l'interface et l'implémentation sont écrites par des personnes
différentes, et les utilisateurs de l'interface en ont accès
bien avant que l'implémentation n'est complète.

Le résultat est beaucoup plus clair qu'un en-tête C++, et
permet des choses assez fines comme le marquage "deprecated"
des membres qu'ils ne faut plus utiliser.


Mais qui lit des en-têtes C++ ? (À part le compilateur,
évidemment.)

Dans les projets les mieux gérés que j'ai fait en C++, les
en-têtes étaient même générés par Rose ; non seulement personne
ne les lisait, personne ne les écrivait. (Dans un projet, avec
Clearcase, les en-têtes étaient déclarés des objets dérivés.
C-à-d qu'il n'existait aucune possibilité même de les modifier à
la main.)

Quand on se sert pas de Rose, la politique est quand même assez
semblable. Le contenu des en-têtes est spécifié par ou en
collaboration avec l'équipe d'architecture ; l'implémentation
dans les sources, évidemment, était la responsabilité unique de
l'implémenteur.

Quant à ne plus modifier une interface, ce n'est qu'un
problème de discipline, et rien n'empêche la discipline en
Java ou l'indiscipline en C++.


C'est certain que la discipline est plus important que le
langage. Mais même avec la discipline la plus rigueureuse, les
erreurs peuvent se produire. Si l'interface est dans un fichier
à part, protégé contre l'écriture, c'est sûr qu'elle sera pas
modifiée. Si elle est dans le même fichier, mais séparé
physiquement (spécifications d'interface en tête, implémentation
après, par exemple), la probabilité d'une modification
accidentale est moindre.

Dans la pratique, plus il y a de vérifications que la discipline
soit suivie, mieux ça vaut. On se plaint déjà de C++ à cet
égard ; il faut bien accès à l'en-tête pour ajouter une variable
ou une fonction privée, par exemple (et du coup, on se sert
beaucoup de l'idiome du pare-feu de compilation). C'est un
problème connu et reconnu en C++ ; pourquoi Java a-t-il voulu
exprès le rendre pire.

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



Avatar
James Kanze
Guillaume Desticourt wrote:
wrote:


Guillaume Desticourt wrote:



[...]


En général, les appels sur des pointeurs à fonction membre ne
sont pas ce qu'il y a de plus rapide. Mais ce n'est pas là la
question. Qu'est-ce qu'il est plus clair :



(this->*pmf)() ;



ma foi, cette solution va reduire le code, mais la syntaxe est
plus lourde qu un if/else donc c est une question de gout...


Moi, j'avoue que cette expression ne me dérange pas. Seulement,
je sais que des expressions semblables dans mon code ont dérangé
des gens qui le lisent plus d'une fois. Et puisque le code est
écrit pour être lu... Je prends en compte les avis de mes
collégues. En général, si les alternatifs sont plus complexes,
ou se prète moins à l'évolution, ils les acceptent. Mais
rarement du premier coup ; s'il y a donc des alternatifs, je les
préfère.

--



if ( c1 ) {
f1() ;
} else {
f2() ;
} // Avec éventuellement plus de cas.



--



switch ( mode ) {
case m1 :
f1() ;
break ;

case m2 :
f2() ;
break ;
} // Avec éventuellement plus de cas.



--



deleguee->f() ;



?



je suppose que c est le cas ou on utilise une strategie(?)
mais comme je ne connais pas je regarde...


Tout à fait. De tous les modèles, c'est probablement celui qui
me sert le plus. C'est, d'ailleurs, un très vieux modèle, que
j'utilisais bien avant d'avoir lu « Design Patterns », et que je
connaissais alors sous le nom de la délégation. D'où le nom de
ma variable:-).

(En passant, en C, j'aurais certainement utilisé le switch.
Pourquoi ne pas en avoir parler aussi ?)



et bien if/else c est pareil que switch et puis il n y a que
deux cas donc if/else me semble bien...


C'est pareil ou non, selon. Le code généré est rarement le
même@; puisque tu t'es concerné des performances, les
performances peuvent être différentes. La lisibilité n'est pas
la même non plus : Si les critères de décision sont clairement
une expression booléenne, l'if exprime mieux ce qu'on fait. Si
en revanche on considère la possibilité de plus d'un cas, que le
choix, par exemple, soit en fonction d'un paramètrage fait par
l'utilisateur, AMHA, le switch exprime mieux l'intention, même
s'il n'y a que deux cas (aujourd'hui).

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


Avatar
James Kanze
Guillaume Desticourt wrote:
wrote:


Guillaume Desticourt wrote:



[...]


j ai une class dont une methode peut changer de comportement
au cours de la vie du process, mais cela rarement. je me
demandais si je devais avoir une methode unique avec un bloc
if/else ou alors un pointeur de methode sette a la methode
qui va bien.




La solution classique ici, c'est la modèle stratégie, non ?



d'apres ce que je lis dans Design Patterns, Catalogue de
modeles de conception reutilisables de Gamma Helm Johnson et
vissides, la strategy sert a remplacer:


switch(_srategie):
case STRATEGY1
behavior1();
break;
case STRATEGY2:
behavior2()
...


par:


_strategie->behavior();


Tout à fait.

donc oui ca a l air de coller, mais:
vu que je dois pouvoir changer de comportement, il faudrait
que _compositeur soit un pointer sur la strategie qui m
interesse, donc dans ma classe j aurais un set de disons 2
strategies differentes, et _strategie serait settee sur un
element de mon set, puis change sur l autre etc...


j aurais donc
- un Strategy * pointant sur un des...
- deux Strategy * (un &Strategy1 et un &Strategy2)


C'est exactement ça. (Sauf que c'est plus fréquent que les
instances des Strategy soient allouées dynamiquement.)

bon ca revient au meme que mon pointer de methode


Pas vraiement. Tes objets de stratégie sont des classes à part.
Ils peuvent, le cas échéant, contenir leur propre données, ce
qui leur donne une souplesse suppémentaire. En revanche, ils ne
peuvent pas, directement, accéder aux données de la classe
appelante ; mais on peut toujours leur passer un pointeur à la
classe appelante, ensuite, soit ils sont amis, soit ils
appellent d'autres fonctions de la classe appelante.

Il y a donc une encapsulation supplémentaire -- parfois, c'est
bien, autrefois, pas. Mais dans la mesure que c'est l'idiome
consacré, c'est ce qu'attend le lecteur, et c'est ce qu'il
comprendra la plus facilement. C'est donc l'idiome à préférer,
faute d'autres critères.

(D'ailleurs, ce n'est pas rare qu'appel d'une fonction virtuelle
soit plus rapide que l'appel à travers un pointeur à fonction
membre.)

si ce n est que au lieu de bosser avec des objets Stratetegy
ma solution de pointer de methode bosser avec des methodes
behavior, et que le behavior est donc dans ma classe
principale au lieu d etre dans une derivee de Strategy. bref
il me semble que c est pareil?


Il y a des ressemblances, certainement. Il y a aussi des
différences, comme tu as bien vu. La grande différence, à mon
avis, c'est que le modèle stratégie est un idiome consacré, à
peu près standard, et que le programmeur qui lit ton code risque
de le reconnaître. Tandis que d'après mon expérience, beaucoup
de programmeurs se sentent mal à l'aise avec des pointeurs à des
fonctions membres.

Est-ce qu'il y a des raisons pour faire autre chose ? D'après
mon expérience :



-- La syntaxe de l'utilisation des pointeurs deplaît à
beaucoup. Moi, je m'en sers de temps en temps, mais chaque
fois, j'ai dû bien en justifier l'utilisation dans les
révues de code. Il faut donc bien une justification pour les
utiliser.



-- L'utilisation des if/else (dans ce cas-ci) risque de donner
des fonctions trop grandes et trop complexes.



d'accord mais dans ce cas, et apres reflexion suite a ce
thread, je me demande si ce n est pas la meilleure solution,
en effet le code du if et du else sont quasi identique, a 4
lignes pres, ainsi, dispatcher le code dans des fonctions
differentes n est peut etre pas pertient notamment au niveau
de la maintenance du code.


Il n'y a pas de solution universelle. En général, si chacun des
branches de l'if/else a à peu près quatre lignes, tu es déjà à
la limite de la complexité d'une fonction. Et rien n'empêche à
les fonctions dans la stratégie d'appeler une fonction commune
pour la partie commune des traitements. D'après mes expériences,
ça donne souvent (mais pas toujours) plus de lisibilité.

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