sho*_*tsy 20 .net c# performance list addrange
分析我的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;
Run Code Online (Sandbox Code Playgroud)
可以说,接口的简单性(只有一个InsertRange的重载)证明了运行时类型切换和转换的性能开销.但是,我指出的3条线路背后的原因是(*)什么?我认为它可以改写为更快的替代方案:
is2.CopyTo(this._items, index);
Run Code Online (Sandbox Code Playgroud)
你认为没有理由不使用这种更简单,更明显更快的替代方案吗?
编辑:
谢谢你的回答.因此,一致意见认为,这是针对以缺陷/恶意方式实施CopyTo的输入集合的保护措施.对我来说,不断付出代价1)运行时类型检查2)临时数组的动态分配3)复制操作的两倍,当所有这些都可以通过定义2或更多的InsertRange重载来保存时一个IEnumerable像现在一样,第二个得到一个List<T>,第三个得到T[].后两个可能已经实现,运行速度是当前情况的两倍.
编辑2:
我确实实现了一个与List相同的类FastList,除了它还提供了一个带有T []参数的AddRange的重载.这种重载不需要动态类型验证和元素的双重复制.我通过向最初为emtpy的列表添加1000次4字节数组,对List.AddRange进行了FastList.AddRange的分析.我的实现比标准List.AddRange的速度快9倍(9!).在我们的应用程序的一个重要使用场景中,List.AddRange占用运行时的大约5%,使用提供更快的AddRange的类替换List可以将应用程序运行时间提高4%.
Sam*_*ell 11
它们阻止了ICollection<T>在插入范围之外访问目标列表的索引的实现.上面的实现导致调用IndexOutOfBoundsException错误(或"操纵")实现CopyTo.
请记住,这T[].CopyTo实际上是内部实现的memcpy,因此添加该行的性能开销很小.当您为大量呼叫增加安全性的成本很低时,您也可以这样做.
编辑:我发现奇怪的部分是在调用后ICollection<T>.CopyTo不会立即调用(复制到临时数组)EnsureCapacity.如果它被移动到该位置,则在任何同步异常之后,列表将保持不变.原样,只有当插入发生在列表的末尾时,该条件才成立.这里的推理是:
Array.Copy不能失败,因为
ICollection.CopyTo以及调整列表大小和分配临时数组所需的分配.如果在移动元素以进行插入之前发生了所有这三个,则更改列表的事务不会抛出同步异常.编辑2(对OP编辑的回应):你有没有对此进行分析?您正在做出一些大胆的声明,即Microsoft应该选择更复杂的API,因此您应该确保在当前方法速度慢的断言中是正确的.我从未遇到过性能方面的问题InsertRange,而且我很确定通过重新设计算法而不是通过重新实现动态列表,可以更好地解决某人遇到的任何性能问题.所以你不要以负面的方式对我采取严厉的态度,请记住以下几点: