OVH Cloud OVH Cloud

Un doute existenciel a propos du parcours de liste dans une boucle 'for'

79 réponses
Avatar
meow
> for ( iterator it=3Dliste.begin() ; it!=3Dliste.end() ; ++it )

Question =E0 deux balles : A tous les coups, le compilo n'a aucun moyen
d'extraire la valeur de liste.end() une fois pour toute avant le d=E9but
de la boucle, du coup =E0 chaque it=E9ration de la boucle le programme se
retape l'appel =E0 liste.end() !
La question toute simple que je me pose est donc : est-ce que c'est pas
mieux de faire

> iterator end=3Dliste.end()
> for ( iterator it=3Dliste.begin() ; it!=3Dend ; ++it )

10 réponses

Avatar
Alain Gaillard



Tu parles des perfs, et tu utilises Java ? :-)


LOL.
Et puis je n'utilise pas Java, on me le fait utiliser....
Subtile nuance :-(


Il parait que le problème de transtypage a été résolu, et que
Java a aussi des templates aujourd'hui. Mais c'était
effectivement un problème dans le temps.


Ah non pas du tout. Il y a les templates certes depuis Java 5.0, mais
qu'est-ce que ça veut dire en Java ? Ca veut dire que tu n'as plus à
écrire les transtypages toi même. Mais voilà. .. les transtypages sont
toujours effectués sous le manteau :-(

--
Alain

Avatar
James Kanze
Sylvain wrote:
Jean-Marc Bourguet wrote on 23/09/2006 07:55:

je suis plutôt de l'avis de Alain, tester le cas
d'auto-affectation me parait même indispensable pour a) ne
pas risquer de perdre les données,


Si le test est nécessaire pour éviter de perdre des données
dans le cas de l'auto-affectation, il y a de gros risques
que le code en fait ne soit pas robustes en présence
d'exceptions.


les 2 peuvent être vrai sans être liés.


Peut-être dans certains cas particuliers. Dans la pratique, on
constate très souvent que si la sémantique exige un test de
auto-affectation, le code ne marche pas en présence
d'exceptions.

....
et devrait être
...
(les if pouvant être mieux utilisés, ce n'est pas le point)



j'aurais peut-être du mettre la remarque précédente en gras
plutôt qu'entre parenthèse.

Pourquoi pas tout simplement:

if (other.ref) other.ref->addRef();
if (ref) ref->release();
ref = other.ref;


les if pouvaient être différents de ceux écrits, il reste et
tu le confirmes que pour ne pas faire un if (*this == other)
tu fais 2 if sur le membre 'ref'.


Tu n'as pas l'air d'avoir compris. Il faut, dans tous les cas,
deux if, un sur l'addRef, et un sur le release. En revanche,
fait comme l'a fait Jean-Marc, le test sur l'auto-affectation
devient inutile. Ce qui est quand même ce dont on parle, non ?

--
James Kanze (Gabi Software) email:
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
Sylvain
James Kanze wrote on 23/09/2006 23:14:

Tu n'as pas l'air d'avoir compris. Il faut, dans tous les cas,
deux if, un sur l'addRef, et un sur le release. En revanche,
fait comme l'a fait Jean-Marc, le test sur l'auto-affectation
devient inutile. Ce qui est quand même ce dont on parle, non ?


tu as vu mon post correctif donc tu sais que "j'ai compris".

mon erreur (au delà de la réponse rapide) était de vouloir donner un
"contre-exemple" (je cite ici Fabien), alors que copie+swap ne
supportent aucun contre-exemple théorique ... (sauf évidemment les
objets non copiables).

comme conclu dans une autre branche du fil, je reste convaincu (par la
pratique) qu'il est parfois pertinent de traiter l'auto-affectation
comme un cas spécifique dans l'utilisation de certains programme - ne
pas les traiter produirait des contre-performances ou de l'obfuscation
fonctionnelle (perçue par l'utilisateur); après ça à chacun ses choix.

Sylvain.

Avatar
James Kanze
Sylvain wrote:
James Kanze wrote on 23/09/2006 23:14:

Tu n'as pas l'air d'avoir compris. Il faut, dans tous les cas,
deux if, un sur l'addRef, et un sur le release. En revanche,
fait comme l'a fait Jean-Marc, le test sur l'auto-affectation
devient inutile. Ce qui est quand même ce dont on parle, non ?


tu as vu mon post correctif donc tu sais que "j'ai compris".


Pas avant d'avoir répandu. Alors, tu peux ignorer ma réponse.

mon erreur (au delà de la réponse rapide) était de vouloir
donner un "contre-exemple" (je cite ici Fabien), alors que
copie+swap ne supportent aucun contre-exemple théorique ...
(sauf évidemment les objets non copiables).

comme conclu dans une autre branche du fil, je reste convaincu
(par la pratique)


Le problème, c'est que tu ne peux pas montrer des cas pratique
qui le justifie. Moi, je sais par la pratique qu'il n'est jamais
nécessaire. (Enfin, je ne crois pas qu'on peut dire « jamais »
quand on parle de ce qu'on a appris de la pratique.) Jusqu'ici,
tous les arguments en sa faveur ont été pûrement théorique. Or,
dans la théorie, je suis d'accord que ce n'est pas beau
d'allouer des ressources et de risquer une exception dans le cas
où ce n'est pas nécessaire (et qu'on peut facilement detecter au
départ que ce n'est pas nécessaire). Je comprends l'argument
d'Alain sur le plan théorique. Mais dans la pratique, qu'est-ce
que ça m'apporte ? C'est un coût supplémentaire, sans aucune
bénifice concrète pour le justifier. Et moi, je travaille dans
l'industrie, et il faut que je justifie les coûts.

--
James Kanze (Gabi Software) email:
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
Alain Gaillard wrote:

[En ce concerne Java...]
Il parait que le problème de transtypage a été résolu, et que
Java a aussi des templates aujourd'hui. Mais c'était
effectivement un problème dans le temps.


Ah non pas du tout. Il y a les templates certes depuis Java
5.0, mais qu'est-ce que ça veut dire en Java ? Ca veut dire
que tu n'as plus à écrire les transtypages toi même. Mais
voilà. .. les transtypages sont toujours effectués sous le
manteau :-(


Parce qu'on ne peut pas modifier le JVM, je sais. Mais est-ce
que ce transtypages-là ne sont plus qu'une question de
performances ? J'aurais au moins espérer que si je déclare
qu'une collection ne contient que des types T, que le
compilateur l'enforce ; que les transtypages de
l'implémentation ne peuvent pas échouer (et que potentiellement,
une bonne JVM saurait reconnaître le cas, et remplacer
l'équivalent d'un dynamic_cast par l'équivalent d'un
static_cast).

--
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
Alain Gaillard


Parce qu'on ne peut pas modifier le JVM, je sais. Mais est-ce
que ce transtypages-là ne sont plus qu'une question de
performances ? J'aurais au moins espérer que si je déclare
qu'une collection ne contient que des types T, que le
compilateur l'enforce ; que les transtypages de
l'implémentation ne peuvent pas échouer


En principe ça ne peut plus échouer, c'est déjà un progrès.

(et que potentiellement,
une bonne JVM saurait reconnaître le cas, et remplacer
l'équivalent d'un dynamic_cast par l'équivalent d'un
static_cast).


Une bonne JVM dis tu ?

Autant que je sache le transtypage est toujours dynamique. Quand tu fais
vector<int>, il y a transtypage en Integer, ils appellent ça le boxing
maintenant, mais c'ets pareil que ce que tu aurais fait à la maint avant
et quand tu veux récupèrer une valeur int il y a ce qu'ils appellent le
unboxing, qui fait pour toi le_int.intValue(); , le_int étant une
instance de Integer donc. (beurk)

--
Alain

Avatar
kanze
Alain Gaillard wrote:

Parce qu'on ne peut pas modifier le JVM, je sais. Mais est-ce
que ce transtypages-là ne sont plus qu'une question de
performances ? J'aurais au moins espérer que si je déclare
qu'une collection ne contient que des types T, que le
compilateur l'enforce ; que les transtypages de
l'implémentation ne peuvent pas échouer


En principe ça ne peut plus échouer, c'est déjà un progrès.


Et que tu as une erreur du compilateur quand tu essaies
d'insérer ou d'extraire le mauvais type d'une collection.

(et que potentiellement,
une bonne JVM saurait reconnaître le cas, et remplacer
l'équivalent d'un dynamic_cast par l'équivalent d'un
static_cast).


Une bonne JVM dis tu ?

Autant que je sache le transtypage est toujours dynamique.


Formellement. Mais c'est comme la machine abstracte de C++ ; tu
peux faire tout ce que tu veux, pourvu que le résultat soit bon.
Les bonnes VM, aujourd'hui, ne se contentent pas de simplement
exécuter les instructions de la VM l'une après l'autre ; elles
prenent des blocs d'instruction, font des suppositions sur la
contexte de l'utilisation, d'après les appels qu'ils ont vus, et
génèrent un code optimal pour cette contexte, avec des
vérifications qu'elle tient en tête. (Si par la suite, une des
vérifications échoue, elle jette le code ainsi généré, et en
génère de nouveau, en faisant moins de suppositions sur la
contexte.)

Quand tu fais vector<int>, il y a transtypage en Integer, ils
appellent ça le boxing maintenant, mais c'ets pareil que ce
que tu aurais fait à la maint avant et quand tu veux récupèrer
une valeur int il y a ce qu'ils appellent le unboxing, qui
fait pour toi le_int.intValue(); , le_int étant une instance
de Integer donc. (beurk)


D'après ce que j'ai compris (mais je n'ai pas suivi en détail,
du tout), les règles ont été conçues exprès d'une façon de
permettre à la VM de découvrir d'une façon assez simple qu'il
s'agit au fond d'un tableau d'un type de base, et de générer le
code en conséquence. De même, s'ils l'ont fait bien, la VM doit
pouvoir « découvrir » que tous les objets qui sont mis dans le
Object[] (qui se trouve à la base de l'implémentation de la
collection) sont en fait des MaClasse, et remplacer le
transtypage dynamique à MaClasse en sortie par un transtypage
statique.

En principe, en tout cas. Dans la pratique, c'est sans doute
comme la principe que le compilateur voit tout ce qui se passe
dans les fonctions inline de std::vector, et que donc, ce n'est
pas la peine d'extraire la fonction end() de la boucle, parce
que le compilateur le fera automatiquement lui-même. (C-à-d que
s'il réussit, c'est seulement dans des cas simples bien
particuliers, dont on se sert pour les benchmarks, mais pas pour
grand chose d'autre.)

--
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
Alain Gaillard

Et que tu as une erreur du compilateur quand tu essaies
d'insérer ou d'extraire le mauvais type d'une collection.


Oui, encore heureux, sinon on se demande à quoi leur templates servirait.
En fait je n'ai pas été clair hier. J'avais encore pas le temps et j'ai
écrit vite. Je vais essayer d'être plus clair dans ce post. En fait tu
ne peux pas faire Vector<int> en Java. Seulement Vector<Integer>. Mais
les conversion de int vers Integer et réciproquement sont faites
automatiquement. Le fameux boxing/unboxing.
Un bout de code pour se fixer les idées:


import java.util.Vector;

import java.util.Vector;


public class Generic {


public Generic(int i) {
}

public static void main(String[] args) {
int i = 2;
Vector< Integer> v = new Vector< Integer >();
v.add(i);

int j = v.get(0);
System.out.println("" + j);
Object o = v.get(0);

// Echec compilation
// Generic g = v.get(0);
// Echec compilation
// Generic g = (Generic) v.get(0);

Vector < Generic > vg = new Vector < Generic >();
vg.add( new Generic(i) );

Generic g = vg.get(0);

}
}

Si tu veux sortir du vector un int, un Integer ou un double par exemple,
ça fonctionnera. Par contre si les types sont incompatibles le
compilateur le détecte.


Formellement. Mais c'est comme la machine abstracte de C++ ; tu
peux faire tout ce que tu veux, pourvu que le résultat soit bon.
Les bonnes VM, aujourd'hui,


Les bonnes. Faut une avoir une....
Tiens je ne sais pas si tu as vu, Sun nous dit que dans sa dernière JVM
1.5.0_07 je crois, ils ont corrigé un memory leak qui se produit à la
fermeture d'un JFrame. Ce memory leak là, je crois bien qu'on le voit
depuis que JFrame existe....


D'après ce que j'ai compris (mais je n'ai pas suivi en détail,
du tout), les règles ont été conçues exprès d'une façon de
permettre à la VM de découvrir d'une façon assez simple qu'il
s'agit au fond d'un tableau d'un type de base, et de générer le
code en conséquence.


Ben non hélas. cf bout de code ci-dessus


De même, s'ils l'ont fait bien, la VM doit
pouvoir « découvrir » que tous les objets qui sont mis dans le
Object[] (qui se trouve à la base de l'implémentation de la
collection) sont en fait des MaClasse, et remplacer le
transtypage dynamique à MaClasse en sortie par un transtypage
statique.




Ben non, toujours pas. D'ailleurs soit dit en passant les tableaux ne
fonctionnent pas avec les génériques.

Voici le byte code pour la fin du code ci-dessus:

63: new #2; //class java/util/Vector
66: dup
67: invokespecial #3; //Method java/util/Vector."<init>":()V
70: astore 5
72: aload 5
74: new #17; //class testgeneric/Generic
77: dup
78: iload_1
79: invokespecial #18; //Method "<init>":(I)V
82: invokevirtual #5; //Method
java/util/Vector.add:(Ljava/lang/Object;)Z
85: pop
86: aload 5
88: iconst_0
89: invokevirtual #6; //Method
java/util/Vector.get:(I)Ljava/lang/Object;
92: checkcast #17; //class testgeneric/Generic
95: astore 6
97: return

Comme tu vois c'est un Object qui est mis dans le tableau et un Object
qui en sort. Puis le transtypage est fait ou plutôt vérifié
dynamiquement avec checkcast, bien qu'il ait été déjà validé à la
compilation.... :-(

(voir les spec de checkcast là
http://java.sun.com/docs/books/vmspec/2nd-edition/html/Instructions2.doc2.html)

Bref les génériques en java c'est plus de l'ordre du sucre syntaxique
qu'autre chose et on est bien loin de C++


En principe, en tout cas. Dans la pratique, c'est sans doute
comme la principe que le compilateur voit tout ce qui se passe
dans les fonctions inline de std::vector, et que donc, ce n'est
pas la peine d'extraire la fonction end() de la boucle, parce
que le compilateur le fera automatiquement lui-même.


Je crains que Java et C++ ne soient bien différents.
En tout cas, Java moi ce n'est pas ma tasse de thé ;)

--
Alain

Avatar
kanze
Alain Gaillard wrote:

[...]
Formellement. Mais c'est comme la machine abstracte de C++ ; tu
peux faire tout ce que tu veux, pourvu que le résultat soit bon.
Les bonnes VM, aujourd'hui,


Les bonnes. Faut une avoir une....
Tiens je ne sais pas si tu as vu, Sun nous dit que dans sa dernière JVM
1.5.0_07 je crois, ils ont corrigé un memory leak qui se produit à la
fermeture d'un JFrame. Ce memory leak là, je crois bien qu'on le voit
depuis que JFrame existe....


Ce n'est pas de la VM, ça. C'est un problème dans la
bibliothèque. (Et je ne sais pas si c'est la même, mais je sais
qu'il y avait des fuites de mémoire quand on fermait une JFrame
déjà quand je faisait du Java. Il y a plus de cinq ans.)

D'après ce que j'ai compris (mais je n'ai pas suivi en détail,
du tout), les règles ont été conçues exprès d'une façon de
permettre à la VM de découvrir d'une façon assez simple qu'il
s'agit au fond d'un tableau d'un type de base, et de générer le
code en conséquence.


Ben non hélas. cf bout de code ci-dessus


Mais justement, une bonne VM analyse le code, fait des
suppositions, et...

De même, s'ils l'ont fait bien, la VM doit
pouvoir « découvrir » que tous les objets qui sont mis dans le
Object[] (qui se trouve à la base de l'implémentation de la
collection) sont en fait des MaClasse, et remplacer le
transtypage dynamique à MaClasse en sortie par un transtypage
statique.


Ben non, toujours pas. D'ailleurs soit dit en passant les tableaux ne
fonctionnent pas avec les génériques.

Voici le byte code pour la fin du code ci-dessus:

63: new #2; //class java/util/Vector
66: dup
67: invokespecial #3; //Method java/util/Vector."<init>":()V
70: astore 5
72: aload 5
74: new #17; //class testgeneric/Generic


Donc, type connu...

77: dup
78: iload_1
79: invokespecial #18; //Method "<init>":(I)V
82: invokevirtual #5; //Method
java/util/Vector.add:(Ljava/lang/Object;)Z


Sauf qu'une bonne VM va plus loin ici. Elle entre dans la
fonction, voit ce qu'elle fait, etc. Et note quelque part,
optimistiquement, que le tableut que contient l'objet ne
contient en fait que des type #17. Chaque fois qu'elle compile
un bout de code, elle vérifie (ou essaie) que cette
post-condition tient avec le bout du code qu'elle a compilée.

85: pop
86: aload 5
88: iconst_0
89: invokevirtual #6; //Method
java/util/Vector.get:(I)Ljava/lang/Object;
92: checkcast #17; //class testgeneric/Generic
95: astore 6
97: return


Et quand elle compile ce bout du code, elle entre dans
Vector.get assez pour voir que la valeur de retour vient bien
d'un tableau d'objet, et si le type est effectivement noté pour
ce tableau, et c'est #17, elle remplace le checkcast par
l'équivalent d'un static_cast (en fait, un no-op si #17 est une
classe, et non une interface). Et évidemment, elle génère une
pré-condition pour ce bout du code qui assure que le type dans
le tableau n'a pas changé.

Selon certains experts avec qui j'ai parlé, c'est en principe
plus facile d'optimiser lors de l'exécution dans un VM que dans
un compilateur classique. Parce que dans la VM, tu as accès réel
du profiling du programme en cours, mais aussi parce que dans la
VM, tu peux faire des optimisations « optimiste », on
supposant par exemple, qu'un pointeur à une Base pointe en fait
toujours au même type dérivé. Tu génères simplement des
préconditions qui fait jeter le code compilé si elles échouent,
pour régénérer avec des nouvelles préconditions, qui prenent en
compte et ce qu'on a vu avant, et la situation qui a fait
échouer la précondition. Évidemment, il existe des compilateurs
classiques C++ qui font pareils, avec les informations du
profiling. Mais si tu as mal choisi les données pour le
profiling, ou si les données changent au fils des années, tu as
un programme qui est optimisé pour une situation atypique.

Comme tu vois c'est un Object qui est mis dans le tableau et
un Object qui en sort.


Mais la VM peut voir plus loins.

Puis le transtypage est fait ou plutôt vérifié
dynamiquement avec checkcast, bien qu'il ait été déjà validé à la
compilation.... :-(


La première fois qu'on l'exécute, probablement. Par la suite,
selon la façon que fonction l'optimisateur de la VM, c'est moins
certain.

(voir les spec de checkcast là
http://java.sun.com/docs/books/vmspec/2nd-edition/html/Instructions2.doc2 .html)

Bref les génériques en java c'est plus de l'ordre du sucre
syntaxique qu'autre chose et on est bien loin de C++


Certes, mais pour d'autres raisons -- je ne crois pas qu'il y a
des paramètres non-type, ni des spécialisations explicites, 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
Alain Gaillard


Ce n'est pas de la VM, ça. C'est un problème dans la
bibliothèque.


Oui c'ets vrai mais pour moi ça ne change pas grand chose quand au fond.
Mon opinion sur Java ne va pas s'en trouver améliorer, c'est ce que je
veux dire.

(Et je ne sais pas si c'est la même, mais je sais
qu'il y avait des fuites de mémoire quand on fermait une JFrame
déjà quand je faisait du Java. Il y a plus de cinq ans.)


Je ne sais pas non plus si c'est le même. Mais ce que je sais c'est que
chaque JMV apporte son lot de bugs puis son lot de correctifs et de
nouveaux bugs et ainsi de suite. On se demande quand même pourquoi la
fermeture d'un JFrame fuit depuis des années.

Mais justement, une bonne VM analyse le code, fait des
suppositions, et...


Franchement je ne sais pas d'où tu sort ça. Si tu as des infos ça
m'intéresse d'ailleurs.
A moins que tu ne parle du JIT ?


Voici le byte code pour la fin du code ci-dessus:



63: new #2; //class java/util/Vector
66: dup
67: invokespecial #3; //Method java/util/Vector."<init>":()V
70: astore 5
72: aload 5
74: new #17; //class testgeneric/Generic



Donc, type connu...


En effet



77: dup
78: iload_1
79: invokespecial #18; //Method "<init>":(I)V
82: invokevirtual #5; //Method
java/util/Vector.add:(Ljava/lang/Object;)Z



Sauf qu'une bonne VM va plus loin ici. Elle entre dans la
fonction, voit ce qu'elle fait, etc. Et note quelque part,
optimistiquement, que le tableut que contient l'objet ne
contient en fait que des type #17. Chaque fois qu'elle compile
un bout de code, elle vérifie (ou essaie) que cette
post-condition tient avec le bout du code qu'elle a compilée.


"Qu'elle compile un bout de code" dit tu, donc tu me parle bien du JIT
là, si je te suis bien. Parce que au niveau du compilateur proprement
dit, je n'en ai jamais vu un qui fait ça.



85: pop
86: aload 5
88: iconst_0
89: invokevirtual #6; //Method
java/util/Vector.get:(I)Ljava/lang/Object;
92: checkcast #17; //class testgeneric/Generic
95: astore 6
97: return



Et quand elle compile ce bout du code, elle entre dans
Vector.get assez pour voir que la valeur de retour vient bien
d'un tableau d'objet, et si le type est effectivement noté pour
ce tableau, et c'est #17, elle remplace le checkcast par
l'équivalent d'un static_cast (en fait, un no-op si #17 est une
classe, et non une interface). Et évidemment, elle génère une
pré-condition pour ce bout du code qui assure que le type dans
le tableau n'a pas changé.


Quand bien même ce que tu me dis serait vrai, (ce n'est pas la question,
je veux bien te croire), tu avoueras que c'est assez laid tout ça non ?


Selon certains experts avec qui j'ai parlé, c'est en principe
plus facile d'optimiser lors de l'exécution dans un VM que dans
un compilateur classique. Parce que dans la VM, tu as accès réel
du profiling du programme en cours,


Je ne suis toujours pas sûr d'avoir bien compris si tu me parles de la
VM proprement dite ou du JIT.
Je sais que Hot Spot fait des optimisations en fonctions des bouts de
code les plus utilisés. Je veux dire que seulement des bouts pertinents
de byte-code sont compilés en des routines assembleurs et que Hot Spot
s'arrange pour éviter de tout compiler ce qui prendrait trop de temps
souvent pour un gain assez faible.

Mais ce que tu me dis semble se passer au niveau de la VM (tu dis
toujours VM) et là franchement je ne sais pas. Moi je croyais même qu'il
y avait toujours transtypages dynamique même après intervention de Hot
Spot, en tous cas au moins jusqu'à la version Hot Spot 1.4. Peut être
qu'avec la 1.5 c'est différent. Si tu as des infos là-dessus, un bon
lien, je suis preneur, parce que tu piques ma curiosité au vif là :-)


Comme tu vois c'est un Object qui est mis dans le tableau et
un Object qui en sort.



Mais la VM peut voir plus loins.


Tu sembles en être tellement convaincu que je vais finir par douter.


Certes, mais pour d'autres raisons -- je ne crois pas qu'il y a
des paramètres non-type, ni des spécialisations explicites, par
exemple.


En effet.

--
Alain