为什么.NET Array类复制如此之慢?

时间:2009-06-10 03:39:21

标签: .net arrays copy performance

我很失望地发现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。

6 个答案:

答案 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应该花费比其他更长的时间,因为内存不是免费的。