Профилирование моего приложения С# показало, что значительное время тратится на List<T>.AddRange
. Использование Reflector для просмотра кода в этом методе показало, что он вызывает List<T>.InsertRange
, который реализован как таковой:
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;
Можно утверждать, что простота интерфейса (только с одной перегрузкой InsertRange) оправдывает накладные расходы на производительность при зашивании и отливке типа времени выполнения.
Но что может быть причиной 3-х строк, которые я указал с помощью (*)
?
Я думаю, что его можно было бы переписать на более быструю альтернативу:
is2.CopyTo(this._items, index);
Вы видите какую-либо причину не использовать эту более простую и, по-видимому, более быструю альтернативу?
Edit:
Спасибо за ответы. Таким образом, мнение консенсуса заключается в том, что это защитная мера против коллекции ввода, осуществляющей CopyTo, с дефектной/вредоносной манерой. Мне кажется, что избыточный уровень постоянно платит цену 1) проверка времени выполнения 2) динамическое распределение временного массива 3) удвоить операцию копирования, когда все это можно было бы сохранить, указав 2 или несколько перегрузок в InsertRange, один получает IEnumerable
, как сейчас, второй получает a List<T>
, третий получает T[]
. Более поздние два могут быть реализованы для работы в два раза быстрее, чем в текущем случае.
Изменить 2:
Я реализовал класс FastList, идентичный List, за исключением того, что он также обеспечивает перегрузку AddRange, которая принимает аргумент T []. Эта перегрузка не требует проверки динамического типа и двойного копирования элементов. Я сделал профиль этого FastList.AddRange для List.AddRange, добавив 4-байтные массивы 1000 раз в список, который изначально был emtpy. Моя реализация превосходит скорость стандартного List.AddRange с коэффициентом 9 (девять!). List.AddRange занимает около 5% времени выполнения в одном из важных сценариев использования нашего приложения, заменяя List классом, обеспечивающим более быструю добавку, чтобы улучшить время выполнения приложений на 4%.