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