.net - 从值类型数组中复制是否比从引用类型数组中复制更快?为什么?

标签 .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[] 数组更快?

最佳答案

对于 ValueApple[]ReferenceApple[];具体差异在很大程度上取决于目标平台;在 x86 上,它似乎在引用类型数组中有一些开销,但在 x64 上就不太明显了(它比 either x86 版本更快,尽管 ReferenceApple[] 版本现在正在复制 两倍 数据):

x86

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

x64

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

我不会说这值得为之费心编写代码。您还需要问:“数组复制是我真正的瓶颈吗?”。如果不是,则此测试完全没有帮助。因此,首先:找出您的瓶颈是什么

某些情况下,结构数组技巧可能会有所帮助,但通常这与非常边缘情况高内存/垃圾-涉及长期存在的海量数据的收集场景。不是包含 20/40 个项目的数组的常规代码。

以上数字基于:

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();
    }
}

包装与不包装:

我质疑你的结果;我的号码:

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

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

这基本上是“大致相同,给予或接受随机 CPU 事件”。超过 5000 万次迭代的几毫秒肯定不值得将代码扭曲变形......

重要的是,确保任何此类测试都处于 Release模式,并在调试器之外执行。

我的测试代码:

using System;
using System.Diagnostics;
struct ValueWrapper<T> {
    public T Value;
}
struct ValueWrapper
{
    public Apple Value;
}
class Apple { }

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

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

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

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

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

关于.net - 从值类型数组中复制是否比从引用类型数组中复制更快?为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13990545/

相关文章:

c++ - 人为限制 C/C++ 内存使用

c# - WPF 获取父窗口

c# - 使用参数中的另一个方法调用调用具有 ConditionalAttribute 的方法

.net - 如何使用 Swashbuckle 在生成的 Swagger 文件中生成全局参数?

c# - 常见内存优化

iphone - 我的 Objective C 代码中只需要一个版本吗?

c# - HANDLE (IntPtr) 已过时,kernel32.dll,SafeFileHandle 到 IntPtr

php - cURL 返回 XML 或 JSON

javascript - 获取对象数组中重复对象的列表

java - Java 数组队列