C# 为什么AddRange比使用foreach循环更快? var fillData=newlist(); 对于(变量i=0;i0) { 此.EnsureCapacity(此._大小+计数); 如果(索引
当我从C# 为什么AddRange比使用foreach循环更快? var fillData=newlist(); 对于(变量i=0;i0) { 此.EnsureCapacity(此._大小+计数); 如果(索引,c#,.net,c#-4.0,C#,.net,C# 4.0,当我从stopwach1和stopwach2获取4结果时,stopwatch1的值总是低于stopwatch2。这意味着addrange总是比foreach快。 有人知道原因吗?因为AddRange检查添加项目的大小,并只增加一次内部数组的大小。如果使用Add,它会根据需要逐渐调整内部数组的大小(加倍),从默认的起始大小10(IIRC)开始。如果您使用: var fillData = new List<int>(); for (var i = 0; i < 100000; i+
stopwach1
和stopwach2
获取4结果时,stopwatch1
的值总是低于stopwatch2
。这意味着addrange
总是比foreach
快。
有人知道原因吗?因为
AddRange
检查添加项目的大小,并只增加一次内部数组的大小。如果使用Add
,它会根据需要逐渐调整内部数组的大小(加倍),从默认的起始大小10(IIRC)开始。如果您使用:
var fillData = new List<int>();
for (var i = 0; i < 100000; i++)
fillData.Add(i);
var stopwatch1 = new Stopwatch();
stopwatch1.Start();
var autoFill = new List<int>();
autoFill.AddRange(fillData);
stopwatch1.Stop();
var stopwatch2 = new Stopwatch();
stopwatch2.Start();
var manualFill = new List<int>();
foreach (var i in fillData)
manualFill.Add(i);
stopwatch2.Stop();
AddRange
可能会检查传递给它的值在哪里实现了IList
或IList
。如果有,它可以找出该范围内有多少个值,因此需要分配多少空间。。。然而,foreach
循环可能需要多次重新分配
此外,即使在分配之后,List
也可以用于向基础阵列执行大容量复制(当然,对于实现IList
的范围)
我怀疑您会发现,如果您再次尝试测试,但对
fillData
使用Enumerable.Range(0,100000)
而不是List
,这两个测试将花费大约相同的时间。使用AddRange
时,集合可以增加数组的大小一次,然后将值复制到数组中
使用foreach
语句,集合需要多次增加集合的大小
增加thr size意味着复制整个数组,这需要时间。AddRange扩展不会遍历每个项,而是将每个项作为一个整体应用。foreach和.AddRange都有其用途。Addrange将赢得针对您当前情况的速度竞赛。在手动添加项目之前,请尝试初始化初始列表容量:
ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
int count = is2.Count;
if (count > 0)
{
this.EnsureCapacity(this._size + count);
// ^^^ this the key bit, and prevents slow growth when possible ^^^
var manualFill=新列表(fillData.Count);
我想这是内存分配优化的结果。
对于AddRange,内存只分配一次,而foreach在每次迭代中都完成了重新分配
在AddRange实现中也可能有一些优化(例如memcpy)列表AddRange方法的Disassembly from reflector具有以下代码
var manualFill = new List<int>(fillData.Count);
ICollection is2=收集为ICollection;
如果(is2!=null)
{
int count=is2.count;
如果(计数>0)
{
此.EnsureCapacity(此._大小+计数);
如果(索引<此大小)
{
复制(此._项,索引,此._项,索引+计数,此._大小-索引);
}
if(this==is2)
{
复制(此._项,0,此._项,索引,索引);
数组.Copy(this._items,(int)(索引+计数),this._items,(int)(索引*2),(int)(this._大小-索引));
}
其他的
{
T[]数组=新的T[计数];
is2.CopyTo(数组,0);
数组.CopyTo(此.\u项,索引);
}
这个。_size+=计数;
}
}
正如您所看到的,有一些优化,如EnsureCapacity()调用和使用Array.Copy()。这就像让服务员十次给您端一瓶啤酒,然后让他一次给您端十瓶啤酒
您认为什么更快:)这是因为Foreach循环将添加循环每次获得一个值的所有值,
AddRange()方法将收集它作为“块”获取的所有值,并立即将该块添加到指定位置
简单地理解一下,这就像你有一个从市场上带来的10件物品的列表,这将更快地一件一件地或一次性带来所有物品。如果你担心GC敏感性能(即在一款针对手机的Unity游戏中),AddRange将安排()传入的集合,这是一种分配。增加容量和手动添加可能会更快。
var manualFill = new List<int>(fillData.Count);
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;
}
}