Frédéric Mélantois
C# 3.0 Beta, déclarations et initialisations simplifiées, regardons sous le capot !
Puisque le produit est encore en beta, portons un regard sur quelques nouveautés du langage et ce qui peut être généré par le compilateur c#.
Par Frédéric Mélantois publié le 25/06/2007 à 00:52, lu 4727 fois, 8 pages
 7 | Explication possible sur « la perte de performance » concernant les initialisations simplifiées de c# 3.0
Nous allons tenter d'expliquer la perte de performance constatée lors des initialisations simplifiées d'objets. Les améliorations du langage se sont faîtes autour du projet « Linq » que nous pourrions définir comme un mini-langage de requête dans le langage. L'instanciation d'objets et initialisations font partie des éléments de celui-ci. Il n'est pas nécessaire de désigner forcément le nom des variables locales. L'équipe c# semble avoir pris le parti de développer un moteur autour des instanciations/initialisations d'objets qui génère à chaque fois une variable locale. De ce fait, lorsque nous utilisons nous-mêmes une variable locale nommée, la variable locale générée par le moteur est évidemment de trop. Le plus simple pour bien comprendre est de prendre un exemple mettant à la fois en évidence l'utilité et l'aberration de générer une variable locale par le moteur créé par l'équipe c# :

public class MaClasse<T> where T : struct

{

    public MaClasse()

    {

    }

    public IList<T> MyProperty { get; set; }

}

 

static void Main(string[] args)

{

    var l = new MaClasse<int> { MyProperty = new List<int> { 1, 2, 3, 4 } };

 

    Console.WriteLine(l.MyProperty[0].ToString());

    Console.ReadLine();

}

Le code IL correspondant à la méthode « Main » est le suivant :

.method private hidebysig static void  Main(string[] args) cil managed

{

  .entrypoint

  .maxstack  3

  .locals init ([0] class ConsoleApplication1.Program/MaClasse`1<int32> l,

           [1] class ConsoleApplication1.Program/MaClasse`1<int32> '<>g__initLocal1', //Variable locale inutile

           [2] class [mscorlib]System.Collections.Generic.List`1<int32> '<>g__initLocal2', //Variable locale utile

           [3] int32 CS$0$0000)

  IL_0000:  newobj    instance void class ConsoleApplication1.Program/MaClasse`1<int32>::.ctor()

  IL_0005:  stloc.1    //Déchargement de la pile dans la variable locale non utile

  IL_0006:  ldloc.1

  IL_0007:  newobj    instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()

  IL_000c:  stloc.2    //Déchargement de la pile vers la variable locale indispensable générée par le compilateur c#

  IL_000d:  ldloc.2

  IL_000e:  ldc.i4.1

  IL_000f:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)

  IL_0014:  ldloc.2

  IL_0015:  ldc.i4.2

  IL_0016:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)

  IL_001b:  ldloc.2

  IL_001c:  ldc.i4.3

  IL_001d:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)

  IL_0022:  ldloc.2

  IL_0023:  ldc.i4.4

  IL_0024:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)

  IL_0029:  ldloc.2

  IL_002a:  callvirt   instance void class ConsoleApplication1.Program/MaClasse`1<int32>::

                                  set_MyProperty(class [mscorlib]System.Collections.Generic.IList`1<!0>)

  IL_002f:  ldloc.1    // Instruction inutile si on avait déchargé au début dans la variable locale désignée

  IL_0030:  stloc.0    // Instruction inutile

  IL_0031:  ldloc.0

  IL_0032:  callvirt   instance class [mscorlib]System.Collections.Generic.IList`1<!0>

                                      class ConsoleApplication1.Program/MaClasse`1<int32>::get_MyProperty()

  IL_0037:  ldc.i4.0

  IL_0038:  callvirt   instance !0 class [mscorlib]System.Collections.Generic.IList`1<int32>::get_Item(int32)

  IL_003d:  stloc.3

  IL_003e:  ldloca.s   CS$0$0000

  IL_0040:  call       instance string [mscorlib]System.Int32::ToString()

  IL_0045:  call       void [mscorlib]System.Console::WriteLine(string)

  IL_004a:  call       string [mscorlib]System.Console::ReadLine()

  IL_004f:  pop

  IL_0050:  ret

}

Le code ci-dessus met en évidence à la fois une variable locale indispensable générée par le compilateur et une variable locale complètement inutile et engendrant une petite perte de performance. D'ailleurs, le lecteur habitué à l'utilisation de l'utilitaire Reflector en trouvera la confirmation ci-dessous :

private static void Main(string[] args)

{

    MaClasse<int> <>g__initLocal2 = new MaClasse<int>();

    List<int> <>g__initLocal3 = new List<int>();

    <>g__initLocal3.Add(1);

    <>g__initLocal3.Add(2);

    <>g__initLocal3.Add(3);

    <>g__initLocal3.Add(4);

    <>g__initLocal2.MyProperty = <>g__initLocal3;

    MaClasse<int> l = <>g__initLocal2;

    Console.WriteLine(l.MyProperty[0].ToString());

    Console.ReadLine();

}

On peut espérer qu'une optimisation du code IL produit puisse être effectuée par l'équipe en charge du compilateur c# sans trop perturber le moteur d'instanciation/initialisation d'objets. Affaire à suivre...
 
» Démarrer une discussion
 
Discussion démarée par Malkuth le 24/07/2007 à 03:11, 2 commentaire(s).
Discussion démarée par vjacquet le 25/06/2007 à 17:42, 4 commentaire(s).