我很失望地发现Array.Clone,Array.CopyTo和Array.Copy都可以被一个简单的3行手动编码(;;)循环击败:
for(int i = 0; i < array.Length; i++)
{
retval[i] = array[i];
}
假设在一个预定大小的数组上执行几百万次操作的基本情况需要10秒。
在每次操作之前使用Array.Clone是非常糟糕的,将时间延长到100秒。
使用Array.CopyTo和Array.Copy只需约45秒。
上面的循环只需要大约20秒。
(忘记这是否会对现实世界产生影响的主观论点,因为在现实世界中,为(>;)循环编写3行代码就像查找数组的文档一样简单类。)
我只是想知道为什么表现如此不同。在旧的C中,最简单的库函数,memcpy()与上面的(;;)循环的手册大致相同,我想知道是否在.NET中实现了这样的其他数组复制函数(;;)的一个很好的简单,没有任何抽象阻碍了Array.Clone,Array.CopyTo和Array.Copy。
答案 0 :(得分:4)
包括分配我得到以下结果:
for loop:104ms
克隆:77毫秒
CopyTo:64ms
以下是代码:
int[] values = new int[16000000];
int[] copiedValues1;
Stopwatch sw = Stopwatch.StartNew();
copiedValues1 = new int[values.Length];
for (int i = 0; i < values.Length; i++)
{
copiedValues1[i] = values[i];
}
Console.WriteLine(sw.ElapsedMilliseconds);
int[] copiedValues2;
sw = Stopwatch.StartNew();
copiedValues2 = (int[])values.Clone();
Console.WriteLine(sw.ElapsedMilliseconds);
int[] copiedValues3;
sw = Stopwatch.StartNew();
copiedValues3 = new int[values.Length];
values.CopyTo(copiedValues3,0);
Console.WriteLine(sw.ElapsedMilliseconds);
答案 1 :(得分:2)
Array方法是否也必须创建和分配输出数组对象?
答案 2 :(得分:1)
您的测试可能有皱纹。快速查看Reflector显示Array.Copy使用externed实现(Array.CopyTo最终使用相同的调用):
[MethodImpl(MethodImplOptions.InternalCall),
ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
internal static extern void Copy(
Array sourceArray,
int sourceIndex,
Array destinationArray,
int destinationIndex,
int length,
bool reliable);
这开启了内存复制与逐项复制的可能性。我自己在发布模式下进行测试,使用int [1000000] - 随机填充,循环时间为468750,而Array.Copy时间为312500。没有太大差异,但weiqure指出仍然更快。
您可能需要调整测试以确保没有其他因素影响结果。
This post对Object数组进行了类似的观察。
答案 3 :(得分:0)
它可能与拳击有关吗?
答案 4 :(得分:0)
你没有提到被复制的数组有多大。也许JIT没有为每种类型的数组专门化Array.Copy和Array.Clone,就像它对泛型类型一样。因此,这些方法首先必须检查数组以确定:它是引用类型还是值类型,如果它是值类型,每个条目有多大?
如果我是对的,复制一个小阵列一千万次将导致这些检查不必要地重复一千万次。另一方面,复制一百万个元素阵列可能比手动编码循环更快,因为Array.Copy可能会对大型阵列采用优化。值得注意的是,在您的手动编码循环中
for(int i = 0; i < array.Length; i++)
{
retval[i] = array[i];
}
JIT将优化数组[i]的范围检查,因为它识别出i始终在范围内;但它可能不会删除retval [i]的范围检查,即使它很容易被证明也在范围内。由于它可能是用本机代码编写的,因此Array.Copy可以避免这些检查。除此之外,Array.Copy可能使用循环展开,对于大字节数组,可能一次复制一个机器字。
但我只是猜想。我不知道如何找出Array.Copy实际上做了什么。
答案 5 :(得分:0)
我想注意Array.Clone()
与其他人有点不同,因为它负责1)分配新数组和2)复制元素,而CopyTo只是复制现成数组的元素。
换句话说,Array.Clone()
必须这样做:
object Clone()
{
var result = new T[this.Length];
this.CopyTo(result, 0);
return result;
}
而像Copy和CopyTo这样的方法只是将元素复制到现有数组中。因此,预计Array.Clone应该花费比其他更长的时间,因为内存不是免费的。