2011-01-03 5 views
5

Je fais des problèmes de pratique à partir de MCTS Exam 70-536 Microsft Application .Net Framework Dev Foundation, et l'un des problèmes est de créer deux classes, un générique, un type d'objet qui effectuent la même chose ; dans lequel une boucle utilise la classe et itérée plus de mille fois. Et en utilisant la minuterie, chronométrer la performance des deux. Il y avait un autre poste à C# generics question qui cherche la même quête mais personne ne répond.Generics vs Performance de l'objet

Fondamentalement, si dans mon code, je lance d'abord la classe générique, il faut que le logeur soit traité. Si je cours la classe d'objet d'abord que la classe d'objet prend plus de temps à traiter. L'idée était de prouver que les génériques fonctionnent plus vite. J'ai utilisé le code des utilisateurs d'origine pour gagner du temps. Je n'ai pas particulièrement vu quelque chose de mal avec le code et a été intrigué par le résultat. Quelqu'un peut-il expliquer pourquoi les résultats inhabituels?

Merci,

Risho

Voici le code:

class Program 
{ 
    class Object_Sample 
    {    
     public Object_Sample() 
     { 
      Console.WriteLine("Object_Sample Class"); 
     } 

     public long getTicks() 
     { 
      return DateTime.Now.Ticks; 
     } 

     public void display(Object a) 
     { 
      Console.WriteLine("{0}", a); 
     } 
    } 

    class Generics_Samle<T> 
    {    
     public Generics_Samle() 
     { 
      Console.WriteLine("Generics_Sample Class"); 
     } 

     public long getTicks() 
     { 
      return DateTime.Now.Ticks; 
     } 

     public void display(T a) 
     { 
      Console.WriteLine("{0}", a); 
     } 
    } 

    static void Main(string[] args) 
    {    
     long ticks_initial, ticks_final, diff_generics, diff_object; 
     Object_Sample OS = new Object_Sample(); 
     Generics_Samle<int> GS = new Generics_Samle<int>(); 

     //Generic Sample 
     ticks_initial = 0; 
     ticks_final = 0; 
     ticks_initial = GS.getTicks(); 

     for (int i = 0; i < 50000; i++) 
     { 
      GS.display(i); 
     } 
     ticks_final = GS.getTicks(); 
     diff_generics = ticks_final - ticks_initial; 

     //Object Sample 
     ticks_initial = 0; 
     ticks_final = 0; 
     ticks_initial = OS.getTicks(); 

     for (int j = 0; j < 50000; j++) 
     { 
      OS.display(j); 
     } 

     ticks_final = OS.getTicks(); 
     diff_object = ticks_final - ticks_initial; 

     Console.WriteLine("\nPerformance of Generics {0}", diff_generics); 
     Console.WriteLine("Performance of Object {0}", diff_object); 

     Console.ReadKey(); 
    } 
} 
+3

Vous devriez accepter les réponses à vos questions. – SLaks

+1

Vous devriez utiliser la classe 'Stopwatch'. – SLaks

+1

Vous devez cliquer sur la coche vide à côté de la meilleure réponse donnée pour chacune de vos questions afin d'accepter cette réponse. – SLaks

Répondre

5

Votre test est incorrect. Voici vos méthodes:

public void display(T a) 
{ 
    Console.WriteLine("{0}", a); // Console.WriteLine(string format, params object[] args) <- boxing is performed here 
} 

public void display(Object a)// <- boxing is performed here 
{ 
    Console.WriteLine("{0}", a); 
} 

Dans les deux cas, vous utilisez la boxe. Beaucoup mieux serait si votre classe, par exemple, comptera somme totale des valeurs, comme:

public void add(long a) 
{ 
    Total += a; 
} 

public void display(Object a)// <- boxing is performed here 
{ 
    Total += (long) a;// <- unboxing is performed here 
} 
+0

Je n'ai pas écrit ce code, je voulais juste tester ce que le livre a suggéré. Dans votre commentaire, vous indiquez que la boxe "public void display (Object a)" est effectuée ici. Pouvez-vous expliquer cela, s'il vous plaît? Je ne cache pas ici, n'est-ce pas? – Risho

+0

'long' est le type de valeur. Lorsque vous lancez le type de valeur à objet, il s'appelle "boxe". Cela signifie que le type de valeur est enveloppé par une 'boîte' spéciale, qui est le type de référence. À ce moment, Net doit allouer de la mémoire pour cette 'boîte' et ce n'est pas une opération si rapide. Lorsque vous convertissez un objet en type valeur, il s'appelle 'unboxing', la valeur est copiée de 'box' vers votre variable. Lorsque vous appelez la méthode avec la signature 'method (objet a)' .Net effectue un cast implicite de votre 'long' à' object' et cela produit un manque de performance à cause de la boxe. –

+0

Ainsi, peu importe le type d'argument d'entrée que vous utilisez dans votre méthode 'display', parce que dans la boîte de dialogue 'object' qui se déroule sur l'appel de méthode' display', et dans 'generic' sur 'Console.WriteLine' appel de méthode - mais il a toujours lieu. –

8

Vous devez exécuter les deux classes un autre moment avant de mettre fin à permettre au JITter de fonctionner.

+0

+1 Cela me semble également être une gigue. –

+0

Avez-vous vérifié son profil pour voir s'il y a des réponses acceptables? –

+1

@Henk - Je pense qu'après 13 questions au moins * une * réponse serait acceptable. Mais encore une fois, l'acceptabilité est dans l'œil du spectateur. –

3

pourquoi serait-il plus rapide? les deux ints doivent être mis en boîte pour pouvoir utiliser Console.WriteLine (string, object)

modifier: ToString() lui-même ne semble pas provoquer la boxe http://weblogs.asp.net/ngur/archive/2003/12/16/43856.aspx

donc lorsque vous utilisez Console.WriteLine (a); ce qui appelle Console.WriteLine (Int32) qui devrait fonctionner je suppose (je devrais regarder dans le réflecteur pour confirmer cela)

+0

Le manuel indique que les génériques sont plus rapides. – Risho

+0

Eh bien, ils sont plus rapides s'ils empêchent la boxe et le déballage, mais ce n'est pas le cas ici – Kikaimaru

+0

J'essaie de comprendre ceci: je ne suis pas convertig ici, sauf Console.WriteLines, que je ne connaissais pas. Si c'est le cas, comment afficher les résultats sans "boxer/unboxing"? – Risho

9

Eh bien, le premier problème que je peux voir est que vous utilisez l'objet DateTime pour mesurer le temps dans votre application (pour un très petit intervalle).

Vous devriez utiliser la classe Stopwatch. Il offre une meilleure précision en essayant de comparer le code.

Le deuxième problème est que vous n'autorisez pas JIT (compilation juste-à-temps). Le premier appel à votre code va prendre plus de temps simplement parce qu'il doit être JIT'd. Après cela, vous obtiendrez vos résultats.

Je ferais un seul appel dans votre code avant de commencer à chronométrer les choses afin que vous puissiez avoir une idée précise de ce qui se passe pendant la boucle.

+1

+1 Pour 'Stopwatch' –

3
  1. Votre code chronométré comprend un Console.WriteLine(). Cela prendra 99,999999% du temps.
  2. Votre hypothèse selon laquelle le générique sera plus rapide dans cette situation est fausse. Vous avez peut-être mal interprété une remarque sur les classes de collection non génériques.
  3. Ce ne sera pas sur l'examen.