从一组值类型复制比从引用类型数组更快?为什么?

时间:2012-12-21 12:50:14

标签: .net arrays memory-management value-type reference-type

我使用一些使用Array.Copy 的测试将数组的部分复制到另一个。

第一个测试在值类型数组

上使用了Array.Copy
struct ValueApple {
    public int Redness;
}

ValueApple[] a1 = ...
ValueApple[] a2 = ...

Array.Copy(a1, a2, a1.Length);

然后第二个测试在引用类型数组

上使用了Array.Copy
class ReferenceApple {
    public int Redness;
}

ReferenceApple[] a1 = ...
ReferenceApple[] a2 = ...

Array.Copy(a1, a2, a1.Length);

我来了,结果显示在值类型数组之间复制比在引用类型数组之间复制更快。

所以我的问题是:

这是真的吗?从一组值类型复制比从一组引用类型复制更快,为什么?


然后我的第二个问题是:

如果从值类型数组中复制更快。如果我为引用类型创建一个值类型的包装器,这不会帮助我提高性能吗?

例如

struct ValueWrapper<T> {
    public T Value;
}

class ReferenceApple { }

ValueWrapper<ReferenceApple>[] a1 = new ValueWrapper<ReferenceApple>[20];
ValueWrapper<ReferenceApple>[] a2 = new ValueWrapper<ReferenceApple>[40];

Array.Copy(a1, a2, 20);

这还应该比使用普通的 ReferenceApple [] 数组快吗?

1 个答案:

答案 0 :(得分:4)

ValueApple[] vs ReferenceApple[];特定的差异很大程度上取决于目标平台;在x86上,它似乎在引用类型数组中有一些开销,但在x64上更不明显(它比 x86版本更快,尽管事实上{{1 }}版本现在正在复制两倍的数据):

86

ReferenceApple[]

64

ValueApple: 3295ms
ValueApple CopyTo: 3283ms
ReferenceApple: 4345ms
ReferenceApple CopyTo: 4234ms

我不会说这是值得折弯的代码。你还需要问:“阵列复制是我真正的瓶颈吗?”。如果不是,那么这个测试完全没有用。首先:找出你的瓶颈

某些情况下,struct-of-struct技巧可能会有所帮助,但通常与非常边缘情况高内存/垃圾更相关涉及长期巨大数据的收集方案。不是包含20/40项数组的常规代码。

以上数字基于:

ValueApple: 1819ms
ValueApple CopyTo: 1864ms
ReferenceApple: 2251ms
ReferenceApple CopyTo: 2335ms

for wrapped vs not:

我对你的结果提出质疑;我的数字:

using System;
using System.Diagnostics;
struct ValueApple
{
    public int Redness;
}
class ReferenceApple
{
    public int Redness;
}

static class Program {
    static void Main()
    {
        const int LOOP = 50000000;

        ValueApple[] a1 = new ValueApple[20];
        ValueApple[] a2 = new ValueApple[40];
        var watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Array.Copy(a1, a2, 20);
        }
        watch.Stop();
        Console.WriteLine("ValueApple: {0}ms", watch.ElapsedMilliseconds);

        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            a1.CopyTo(a2, 0);
        }
        watch.Stop();
        Console.WriteLine("ValueApple CopyTo: {0}ms", watch.ElapsedMilliseconds);

        ReferenceApple[] a3 = new ReferenceApple[20];
        ReferenceApple[] a4 = new ReferenceApple[40];
        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            Array.Copy(a3, a4, 20);
        }
        watch.Stop();
        Console.WriteLine("ReferenceApple: {0}ms", watch.ElapsedMilliseconds);

        watch = Stopwatch.StartNew();
        for (int i = 0; i < LOOP; i++)
        {
            a3.CopyTo(a4, 0);
        }
        watch.Stop();
        Console.WriteLine("ReferenceApple CopyTo: {0}ms", watch.ElapsedMilliseconds);

        Console.WriteLine("(done)");
        Console.ReadKey();
    }
}

(最后一个版本是非通用版本)

基本上“大致相同,给予或采取随机CPU活动”。超过50M迭代的几毫秒肯定没有什么值得折弯的代码...

重要的是,确保所有此类测试都处于发布模式,并在调试器外部执行。

我的测试代码:

Wrapper<T>: 2175ms
Direct: 2231ms
Wrapper: 2165ms