Rapidité de lecture d'une variable

Rapidité de lecture d'une variable - C#/.NET managed - Programmation

Marsh Posté le 13-04-2004 à 20:35:29    

Salut
 
J'avais une question simple concerant deux manière d'accéder à une variable.
 
La première manière est celle-ci :
((Ray)list[0]).length
Cette accès se fait plusieurs fois.
 
La deuxième manière est celle-ci :
double toto = ((Ray)list[0]).length; (créer une fois)
lire plusieurs fois toto.
 
Je me demandais si le fait de caster pouvait ralentir la vitesse de mon prgramme.
 
Merci


---------------
Qui avale une noix de coco fait confiance à son anus...
Reply

Marsh Posté le 13-04-2004 à 20:35:29   

Reply

Marsh Posté le 14-04-2004 à 15:02:16    

Comme Lenght est surement une "Property", elle va être évaluée à chaque itération.
La deuxième Méthode est donc la plus rapide.
 
en revanche cette méthode est déconseillée dans un environnement multi-threadé
 
Le cast n'a pas grand chose à voir la dedans (enfin je pense :) )


Message édité par fli le 14-04-2004 à 15:04:40
Reply

Marsh Posté le 14-04-2004 à 15:11:24    

bah c'est pour ça que y a un truc qu'i s'appelle compilateur :o
 
pour un tableau dont la taille est constante, ça coute rien du tout. ce qui fait qu'entre le for, le for avec Length dans une variable et le foreach, je préfèrerais le foreach ou le for, le for avec constante n'étant d'aucune utilité


Message édité par Taz le 14-04-2004 à 15:14:57
Reply

Marsh Posté le 14-04-2004 à 15:24:01    

Taz a écrit :

bah c'est pour ça que y a un truc qu'i s'appelle compilateur :o
 
pour un tableau dont la taille est constante, ça coute rien du tout. ce qui fait qu'entre le for, le for avec Length dans une variable et le foreach, je préfèrerais le foreach ou le for, le for avec constante n'étant d'aucune utilité


 
Effectivement le foreach est très élégant et très pratique mais il consomme beaucoup plus.
 
En ce qui concerne la question d'Imhotep je confirme mon point de vue : L'accès à une propriété en .NET est en fait un appel à une fonction (comme en COM) je pense donc que l'appel d'une fonction est toujours plus long que l'accès direct à une variable y compris quand la fonction est inline.
 

Reply

Marsh Posté le 14-04-2004 à 17:26:22    

non, il ne consomme pas beaucoup plus.
 
un foreach sera dans beaucoup de cas la manière la plus rapide et permet au compilateur beaucoup d'optimisation.
 
sur un Array, le foreach égale à peu près le for, sur un ArrayList, il lui fou une claque monumentale

Reply

Marsh Posté le 14-04-2004 à 19:26:12    

Si par exemple j'ai une propriété comme suit :
 

Code :
  1. public double Length
  2. {
  3. get
  4. {
  5.  return length;
  6. }
  7. set
  8. {
  9.  length = value;
  10. }
  11. }


 
Est-ce qu'il va y avoir un appel de fonction (ralentir le programme ?) ou bien le compilateur va optimiser tout ça ?


---------------
Qui avale une noix de coco fait confiance à son anus...
Reply

Marsh Posté le 14-04-2004 à 20:30:53    

le compilateur va pouvoir optimiser. regarder le msil pour en savoir plus. mais en aucun cas il ne faut faire de la parano, utiliser le foreach autant que possible
 
 
let set_Length je le sens mal là :D

Reply

Marsh Posté le 14-04-2004 à 20:52:34    

Taz a écrit :

le compilateur va pouvoir optimiser. regarder le msil pour en savoir plus. mais en aucun cas il ne faut faire de la parano, utiliser le foreach autant que possible


Ok merci.
J'ai pas besoin du foreach dans ce cas là.
 

Citation :

let set_Length je le sens mal là :D


Pourquoi ?


---------------
Qui avale une noix de coco fait confiance à son anus...
Reply

Marsh Posté le 14-04-2004 à 20:56:45    

Imhotep a écrit :


Ok merci.
J'ai pas besoin du foreach dans ce cas là.

pense pas comme ça. utilise le foreach puis le for si tu en as vraiment besoin
 

Imhotep a écrit :


Citation :

let set_Length je le sens mal là :D


Pourquoi ?

euh il manque un peu de code ...

Reply

Marsh Posté le 14-04-2004 à 21:18:55    

Taz a écrit :

pense pas comme ça. utilise le foreach puis le for si tu en as vraiment besoin


Ok  :)  
 

Taz a écrit :

euh il manque un peu de code ...


Je vois pas....  :??:  Ca marche très bien comme ça...


---------------
Qui avale une noix de coco fait confiance à son anus...
Reply

Marsh Posté le 14-04-2004 à 21:18:55   

Reply

Marsh Posté le 14-04-2004 à 21:21:13    

je sais bien, je veux juste dire que si fais ta propre Collection, this.length=value; ça redimensionnera sans doute pas grand chose :P

Reply

Marsh Posté le 14-04-2004 à 21:53:08    

Taz a écrit :

je sais bien, je veux juste dire que si fais ta propre Collection, this.length=value; ça redimensionnera sans doute pas grand chose :P


ok  :)  
Mais c'est pas pour une collection :) En tout cas, merci pour tout ces conseils.


Message édité par Imhotep le 14-04-2004 à 21:54:40

---------------
Qui avale une noix de coco fait confiance à son anus...
Reply

Marsh Posté le 15-04-2004 à 23:52:00    

Taz a écrit :

le compilateur va pouvoir optimiser. regarder le msil pour en savoir plus. mais en aucun cas il ne faut faire de la parano, utiliser le foreach autant que possible


 
Pour le foreach je suis d'accord avec toi, pour les conteneurs complex Arraylist, ... c'est clair que c'est mieux!
 
 :non:  
En revanche pour la problème d'origine d'Imhotep, à savoir sortir le (...).Lengh de sa boucle, là je ne le suis pas!
 
Et pour en être certain, j'ai fait ce que tu as proposé:
 
j'ai compilé le petit pgm C# suivant :

Code :
  1. ArrayList myAL = new ArrayList();
  2.   myAL.Add("Hello" );
  3.   myAL.Add("World" );
  4.   myAL.Add("!" );
  5.   // test 1
  6.   for(int i = 0; i < myAL.Count; ++i)
  7.   {
  8.    Console.Write(myAL[i]);
  9.   }
  10.   // test 2
  11.   int b = myAL.Count;
  12.   for(int i = 0; i < b; ++i)
  13.   {
  14.    Console.Write(myAL[i]);
  15.   }


bien sur en release avec les optimisations sur ON
Et j'ai DeMSILé (pardon pour le nom je ne savais pas quoi mettre :) ) voici ce que ça donne :

Code :
  1. .entrypoint
  2.     .locals init (class [mscorlib]System.Collections.ArrayList V0,
  3.                   int32 V1,
  4.                   int32 V2,
  5.                   int32 V3)
  6.     newobj void [mscorlib]System.Collections.ArrayList::.ctor()
  7.     stloc.0
  8.     ldloc.0
  9.     ldstr "Hello"
  10.     callvirt int32 [mscorlib]System.Collections.ArrayList::Add(class System.Object)
  11.     pop
  12.     ldloc.0
  13.     ldstr "World"
  14.     callvirt int32 [mscorlib]System.Collections.ArrayList::Add(class System.Object)
  15.     pop
  16.     ldloc.0
  17.     ldstr "!"
  18.     callvirt int32 [mscorlib]System.Collections.ArrayList::Add(class System.Object)
  19.     pop
  20.     ldc.i4.0
  21.     stloc.1
  22.     br.s loc_4E
  23. loc_3E:                                 // CODE XREF: Main+45j
  24.     ldloc.0
  25.     ldloc.1
  26.     callvirt class System.Object [mscorlib]System.Collections.ArrayList::get_Item(int32)
  27.     call void [mscorlib]System.Console::Write(class System.Object)
  28.     ldloc.1
  29.     ldc.i4.1
  30.     add
  31.     stloc.1
  32. loc_4E:                                 // CODE XREF: Main+2Cj
  33.     ldloc.1
  34.     ldloc.0
  35.     callvirt int32 [mscorlib]System.Collections.ArrayList::get_Count()
  36.     blt.s loc_3E
  37.     ldloc.0
  38.     callvirt int32 [mscorlib]System.Collections.ArrayList::get_Count()
  39.     stloc.2
  40.     ldc.i4.0
  41.     stloc.3
  42.     br.s loc_72
  43. loc_62:                                 // CODE XREF: Main+64j
  44.     ldloc.0
  45.     ldloc.3
  46.     callvirt class System.Object [mscorlib]System.Collections.ArrayList::get_Item(int32)
  47.     call void [mscorlib]System.Console::Write(class System.Object)
  48.     ldloc.3
  49.     ldc.i4.1
  50.     add
  51.     stloc.3
  52. loc_72:                                 // CODE XREF: Main+50j
  53.     ldloc.3
  54.     ldloc.2
  55.     blt.s loc_62
  56.     ret


 
On peut facilement voire que pour la condition de sortie du test1 :

Code :
  1. loc_4E:                                 // CODE XREF: Main+2Cj
  2.     ldloc.1
  3.     ldloc.0
  4.     callvirt int32 [mscorlib]System.Collections.ArrayList::get_Count()
  5.     blt.s loc_3E


l'appel à get_Count ( qui correspond à la lecture de la propriété ) est effectué à chaque fois. Ce qui est d'ailleurs normal pour les environnements multi-thread.
 
Le compilateur n'a rien optimisé même si l'attribut [STAThread]était présent à la compilation.  
 
C'est vrai aussi que ça ne va pas beaucoup changer les choses, mais par contre ça montre bien que les compilos ne font pas tout ...

Reply

Marsh Posté le 16-04-2004 à 00:03:14    

certes : mais bon c'est minime, les compilateurs sont encore jeunes etc ...
 
mais bon de toutes façons tu donnes ici un bon contre-exemple vu que [] de ArrayList fait une vérification sur l'index

Reply

Marsh Posté le 16-04-2004 à 00:28:21    

ce n'est pas un contre-exemple  :pt1cable: c juste pour montrer le problème de la variable dans le for
 
pour le for/foreach ça dépend plus des données et surtout du conteneur manipulés que d'autre chose, enfin à mon sens
 
De toute façon c'était sympa de fouiner ds le MSIL :bounce:

Reply

Marsh Posté le 16-04-2004 à 03:13:23    

enfin là tu voit bien qu'avec ArrayList l'utilisation du foreach serait nettement plus rapide

Reply

Marsh Posté le 16-04-2004 à 09:09:15    

c'est un bon contre exemple :o

Reply

Marsh Posté le 16-04-2004 à 10:14:13    

oui oui
j'aurais du prendre un bete tableau, pour l'exemple. Mais ArrayList ou autre chose ce n'est pas ce qui m'intéressait, j'en était toujours à la question de base et ne parlais que du for
 
Enfin j'espère qu'Imhotep va s'y retrouver dans tout ça  :lol:

Reply

Marsh Posté le 16-04-2004 à 13:32:18    

Oui je pense que je devrais pouvoir m'en sortir.  :)  
 
Par contre, j'ai essayé de remplacer for par foreach que cela était possible dans une classe utilisant un ArrayList, et c'est plus lent... Avec un for, le rendu d'une image pe prenait 36.12 sec en moyenne et avec un foreach, ca monte à 39.5 sec.
 
Avant :
 

Code :
  1. for (int i = 0; i < ll.Count; i++)
  2. {
  3. ((csgIntersections)ll[i]).Ray = left.toAbsoluteRay(((csgIntersections)ll[i]).Ray);
  4. ((csgIntersections)ll[i]).Ray.Length = (ray.Source - ((csgIntersections)ll[i]).Ray.Source).norm();
  5. }


 
Après :
 

Code :
  1. foreach (csgIntersections csg in lr)
  2. {
  3. csg.Ray = left.toAbsoluteRay(csg.Ray);
  4. csg.Ray.Length = (ray.Source - csg.Ray.Source).norm();
  5. }


---------------
Qui avale une noix de coco fait confiance à son anus...
Reply

Marsh Posté le 16-04-2004 à 13:33:06    

enfin il faut surtout retenir que le foreach est une très bonne chose et que seuls les idiots paranos continueront à écrire du code sale blindé de for.

Reply

Marsh Posté le 17-04-2004 à 02:01:00    

quelqu'un aurait une spécification complète du MSIL ?
 
      ldloc.3
      ldc.i4.1
      add
      stloc.3
 
pour un pauvre ++i c'est un peu long quand même ...

Reply

Marsh Posté le 18-04-2004 à 04:11:16    

up

Reply

Marsh Posté le 18-04-2004 à 08:30:32    

Reply

Marsh Posté le 19-04-2004 à 15:54:10    

Taz a écrit :


      ldloc.3
      ldc.i4.1
      add
      stloc.3


 
C'est toujours un peu pareil avec ces langages intermédières qui sont basés sur une "operand stack" (OS) :
 
ldloc.3        charge une variable sur l'OS
ldc.i4.1       charge la constante 1 sur l'OS
add            ajoute les 2 éléments les plus hauts sur l'OS
stloc.3        sauvegarde la nouvelle valeur de l'OS dans la variable local correspondante


Message édité par fli le 19-04-2004 à 15:54:49
Reply

Marsh Posté le 19-04-2004 à 16:09:02    

certes, j'ai pas dit le contraire, mais bon, prévoir un petit cas particulier, ça peut servir un peu dans même

Reply

Marsh Posté le 19-04-2004 à 16:33:44    

Taz a écrit :

certes, j'ai pas dit le contraire, mais bon, prévoir un petit cas particulier, ça peut servir un peu dans même


De ce que j'ai pu voire, le gros des optimisations est fait lors de la compilation du MSIL vers le code natif, dans cet exemple la section  

Code :
  1. ldloc.3
  2. ldc.i4.1
  3. add
  4. stloc.3

est transformé en un "bète"

Code :
  1. inc EBX

la variable i étant constamment maintenu dans un registre.

Reply

Marsh Posté le 19-04-2004 à 16:50:27    

ben j'espère bien que la compilation JIT travaille bien. cela dit... si tu utilises un interpréteur ou autre ...

Reply

Marsh Posté le 19-04-2004 à 16:55:05    

C'est sure!
Je ne sais pas où en est mono:: mais en environnement M$ de toute façon tu passes par le JIT

Reply

Marsh Posté le 19-04-2004 à 16:56:33    

mono aussi. seulement y a mint aussi, l'interpréteur. MS s'en fout, il bosse pour une seule architecture ... quand tu en as des dizaines à couvrir, c'est moins marrant ... donc mint est d'abord porté puis mono

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed