Listar a implementação <T> .AddRange abaixo do ideal
A criação de perfil do meu aplicativo C # indicou que um tempo significativo é gasto emList<T>.AddRange
. O uso do Refletor para examinar o código neste método indicou que ele chamaList<T>.InsertRange
que é implementado como tal:
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;
Pode-se argumentar que a simplicidade da interface (com apenas uma sobrecarga de InsertRange) justifica a sobrecarga de desempenho da verificação e conversão do tipo de tempo de execução. Mas qual poderia ser a razão por trás das três linhas que indiquei com(*)
? Eu acho que poderia ser reescrito para a alternativa mais rápida:
is2.CopyTo(this._items, index);
Você vê algum motivo para não usar essa alternativa mais simples e aparentemente mais rápida?
Editar:
Obrigado pelas respostas. Portanto, a opinião do consenso é que essa é uma medida protetora contra a coleta de entradas que implementa o CopyTo de maneira defeituosa / maliciosa. Para mim, parece um exagero pagar constantemente o preço de 1) verificação do tipo de tempo de execução 2) alocação dinâmica da matriz temporária 3) duplicar a operação de cópia, quando tudo isso poderia ter sido salvo ao definir 2 ou mais sobrecargas de InsertRange um recebendoIEnumerable
como agora, o segundo recebendo umList<T>
terceiro obtendoT[]
. Os dois últimos poderiam ter sido implementados para rodar duas vezes mais rápido do que no caso atual.
Edição 2:
Eu implementei uma classe FastList, idêntica à List, exceto que ela também fornece uma sobrecarga de AddRange, que recebe um argumento T []. Essa sobrecarga não precisa da verificação dinâmica do tipo e da cópia dupla de elementos. Criei o perfil desse FastList.AddRange em relação a List.AddRange adicionando matrizes de 4 bytes 1000 vezes a uma lista que estava inicialmente vazia. Minha implementação supera a velocidade do padrão List.AddRange com um fator de 9 (nove!). O List.AddRange ocupa cerca de 5% do tempo de execução em um dos cenários de uso mais importantes do nosso aplicativo. Substituir List por uma classe que fornece um AddRange mais rápido pode melhorar o tempo de execução do aplicativo em 4%.