Pourquoi List .Enumerator est-il plus rapide que mon implémentation?

Je me suis retrouvé dans une position où je devais lancer ma propre implémentation de tableau dynamic, en raison de nombreux avantages en termes de performances (dans mon cas). Cependant, après avoir créé un énumérateur pour ma version et comparé l’efficacité avec celle utilisée par List, je suis un peu déconcerté; la liste est environ 30-40% plus rapide que ma version, même si c’est beaucoup plus complexe.

Voici la partie importante de l’implémentation de l’énumérateur de liste:

public struct Enumerator : IEnumerator, IDisposable, IEnumerator { private List list; private int index; private int version; private T current; internal Enumerator(List list) { this.list = list; this.index = 0; this.version = list._version; this.current = default(T); return; } public bool MoveNext() { List list; list = this.list; if (this.version != list._version) { goto Label_004A; } if (this.index >= list._size) { goto Label_004A; } this.current = list._items[this.index]; this.index += 1; return 1; Label_004A: return this.MoveNextRare(); } public T Current { get { return this.current; } } } 

Et voici ma version très barebone:

 internal struct DynamicArrayEnumerator : IEnumerator where T : class { private readonly T[] internalArray; private readonly int lastIndex; private int currentIndex; internal DynamicArrayEnumerator(DynamicArray dynamicArray) { internalArray = dynamicArray.internalArray; lastIndex = internalArray.Length - 1; currentIndex = -1; } public T Current { get { return internalArray[currentIndex]; } } public bool MoveNext() { return (++currentIndex <= lastIndex); } } 

Je sais que c’est de la micro-optimisation, mais j’aimerais vraiment comprendre pourquoi l’agent recenseur est tellement plus rapide que le mien. Des idées? Merci!

Edit: comme demandé; la classe DynamicArray (les parties pertinentes): l’énumérateur est une classe interne.

 public struct DynamicArray : IEnumerable where T : class { private T[] internalArray; private int itemCount; internal T[] Data { get { return internalArray; } } public int Count { get { return itemCount; } } public DynamicArray(int count) { this.internalArray = new T[count]; this.itemCount = 0; } public IEnumerator GetEnumerator() { return new DynamicArrayEnumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } 

En ce qui concerne comment je teste:

  List list = new List(1000000); DynamicArray dynamicArray = new DynamicArray(1000000); // Code for filling with data omitted. int numberOfRuns = 0; float p1Total = 0; float p2Total = 0; while (numberOfRuns  { int u = 0; foreach (BaseClass b in list) { if (bB > 100) // Some sortingvial task u++; } }); p1.ExecuteAndClock(); p1Total += p1.TotalElapsedTicks; PerformanceAnalyzer p2 = new PerformanceAnalyzer(() => { int u = 0; foreach (BaseClass b in dynamicArray) { if (bB > 100) // Some sortingvial task u++; } }); p2.ExecuteAndClock(); p2Total += p2.TotalElapsedTicks; numberOfRuns++; } Console.WriteLine("List enumeration: " + p1Total / totalRuns + "\n"); Console.WriteLine("Dynamic array enumeration: " + p2Total / totalRuns + "\n"); 

La classe PerformanceAnalyzer démarre un chronomètre, exécute le délégué Action fourni, puis arrête le chronomètre par la suite.

Edit 2 (Réponse rapide à Ryan Gates): Il existe plusieurs raisons pour lesquelles je voudrais lancer la mienne. Plus important encore, j’ai besoin d’une méthode RemoveAt (int index) très rapide.

Etant donné que je n’ai pas à m’inquiéter de l’ordre des éléments de la liste dans mon cas particulier, je peux éviter la manière de procéder de la liste intégrée .Net:

 public void RemoveAt(int index) { T local; if (index = this._size) { goto Label_0042; } Array.Copy(this._items, index + 1, this._items, index, this._size - index); Label_0042: this._items[this._size] = default(T); this._version += 1; return; } 

Et à la place, utilisez quelque chose comme:

 public void RemoveAt(int index) { // overwrites the element at the specified index with the last element in the array and decreases the item count. internalArray[index] = internalArray[itemCount]; itemCount--; } 

Économiser potentiellement énormément de temps dans mon cas, si disons les 1000 premiers éléments d’une longue liste doivent être supprimés par index.

Outre les problèmes d’parsing comparative, voici comment vous pouvez rendre votre classe DynamicArray plus semblable à List :

 public DynamicArrayEnumerator GetEnumerator() { return new DynamicArrayEnumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } 

Désormais, le code qui sait travailler avec un tableau dynamic peut effectuer une itération avec un DynamicArrayEnumerator sans boxing ni envoi virtuel . C’est exactement ce que fait la List . Le compilateur notifie lorsqu’un type implémente le modèle de manière personnalisée et utilisera les types impliqués à la place des interfaces.

Avec votre code actuel , vous ne tirez aucun avantage de la création d’une struct , car vous la GetEnumerator() dans GetEnumerator() .

Essayez le changement ci-dessus et corrigez le sharepoint référence pour qu’il fonctionne plus longtemps. Je m’attendrais à voir une grande différence.