对我的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重载)可以证明运行时类型检查和强制类型转换的性能开销。 但是,我标记的这3行代码有什么原因呢? 我认为它可以重写为更快的替代方案:
is2.CopyTo(this._items, index);
您看到使用这种更简单、看起来更快的替代方案没有任何理由不使用吗?
编辑:
感谢回答。因此,共识意见是这是一项防护措施,针对实现CopyTo的输入收集方式存在缺陷/恶意的情况。对我来说,似乎不必要地付出了以下代价:1)运行时类型检查 2)暂存数组的动态分配 3)复制操作加倍,当定义了2个或多个重载的InsertRange时,所有这些都可以得到节省,其中一个获取IEnumerable
,第二个获取List<T>
,第三个获取T[]
。后两者可以实现两倍于当前情况的运行速度。
编辑2:
我实现了一个类FastList,与List相同,除了它还提供了一个接受T[]参数的AddRange的重载。这个重载不需要动态类型验证和双重复制元素。我通过将4字节数组1000次添加到最初为空的列表中来测试了这个FastList.AddRange。我的实现以9(九!)倍于标准List.AddRange的速度击败了它。在我们应用程序的一个重要使用场景中,List.AddRange占有大约5%的运行时间,用提供更快的AddRange的类替换List可以提高应用程序运行时间4%。
Insert
插入单个int
,而不是使用InsertRange
插入4字节数组,你会得到更大的提升。 - Sam Harwell