Listet die <T> .AddRange-Implementierung suboptimal auf
Bei der Profilerstellung für meine C # -Anwendung wurde festgestellt, dass viel Zeit in verbracht wurdeList<T>.AddRange
. Wenn Sie Reflector verwenden, um den Code in dieser Methode zu betrachten, wird der Aufruf angezeigtList<T>.InsertRange
welches als solches implementiert ist:
public void InsertRange(int index, IEnumerable<T> collection)
{
if (collection == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
}
if (index > this._size)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index);
}
ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
int count = is2.Count;
if (count > 0)
{
this.EnsureCapacity(this._size + count);
if (index < this._size)
{
Array.Copy(this._items, index, this._items, index + count, this._size - index);
}
if (this == is2)
{
Array.Copy(this._items, 0, this._items, index, index);
Array.Copy(this._items, (int) (index + count), this._items, (int) (index * 2), (int) (this._size - index));
}
else
{
T[] array = new T[count]; // (*)
is2.CopyTo(array, 0); // (*)
array.CopyTo(this._items, index); // (*)
}
this._size += count;
}
}
else
{
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
this.Insert(index++, enumerator.Current);
}
}
}
this._version++;
}
private T[] _items;
Man kann argumentieren, dass die Einfachheit der Schnittstelle (nur eine Überladung von InsertRange) den Performance-Overhead von Laufzeit-Typ-Cheching und -Casting rechtfertigt. Aber was könnte der Grund für die 3 Zeilen sein, die ich angegeben habe(*)
? Ich denke, es könnte auf die schnellere Alternative umgeschrieben werden:
is2.CopyTo(this._items, index);
Sehen Sie einen Grund, diese einfachere und anscheinend schnellere Alternative nicht zu nutzen?
Bearbeiten:
Danke für die Antworten. Konsens Meinung ist also, dass dies eine Schutzmaßnahme gegen die Eingabe-Sammlung ist, die das CopyTo in einer fehlerhaften / böswilligen Weise implementiert. Für mich scheint es ein Overkill zu sein, ständig den Preis für 1) Laufzeitüberprüfung 2) dynamische Zuordnung des temporären Arrays 3) doppelten Kopiervorgang zu zahlen, wenn dies alles durch die Definition von 2 oder mehr Überladungen von InsertRange hätte gespeichert werden können man bekommtIEnumerable
wie jetzt bekommt der zweite eineList<T>
drittes bekommenT[]
. Die beiden letzteren könnten so implementiert sein, dass sie doppelt so schnell laufen wie im aktuellen Fall.
Bearbeiten 2:
Ich habe eine Klasse FastList implementiert, die mit List identisch ist, mit der Ausnahme, dass sie auch eine Überladung von AddRange enthält, die ein T [] -Argument akzeptiert. Diese Überladung erfordert keine dynamische Typüberprüfung und kein doppeltes Kopieren von Elementen. Ich habe diese FastList.AddRange gegen List.AddRange profiliert, indem ich einer Liste, die anfänglich leer war, 1000-mal 4-Byte-Arrays hinzugefügt habe. Meine Implementierung übertrifft die Geschwindigkeit von Standard List.AddRange um den Faktor 9 (neun!). List.AddRange benötigt ungefähr 5% der Laufzeit in einem der wichtigsten Verwendungsszenarien unserer Anwendung. Wenn Sie List durch eine Klasse mit einem schnelleren AddRange ersetzen, kann die Laufzeit der Anwendung um 4% verbessert werden.