MemberwiseClone相当于现有对象?

时间:2013-08-06 17:44:11

标签: c# performance memory-management

这里有很多关于MemberwiseClone的问题,但我找不到任何涉及这一点的内容。

据我了解,MemberwiseClone基本上只复制一个对象的内存区域,将其转储到其他地方并将其称为该对象的新实例。显然非常快,对于大型物体来说,这是制作副本的最快捷方式。对于具有简单构造函数的小对象,可以更快地创建实例并复制各个属性。

现在,我有一个紧凑的循环,我正在使用MemberwiseClone。由于MemberwiseClone总是创建一个新实例,这会导致大量的内存分配和重新分配,这对性能来说并不是很好。我为一个非常大的对象编写了一个自定义副本,它将所有属性分别复制到现有对象上,总体上比使用MemberwiseClone快一点,

我觉得如果我能抓住整块内存并将其转储到现有对象上,我就会获得不错的性能提升,因为GC不必担心任何事情。希望我也能摆脱这条消息:

为了澄清,我想尽快将属性从一个现有对象复制到另一个对象。我只需要一份浅色的副本。

  

消息2 DA0023 :(平均)GC中的%时间= 19.30;         GC中的%时间相对较高。这种过量垃圾收集开销的迹象可能会影响到   您的应用程序的响应性。您可以收集.NET内存   分配数据和对象生命周期信息来理解   应用程序使用的内存分配模式更好。

我对代码安全没有限制,速度是游戏的目标。

没有评论询问这是否真的有必要或者请讨论过早优化。

感谢您的帮助。

答案

采用以下建议,在对象中嵌入结构并在其中存储所有属性。然后我可以获取该结构的副本以复制单个赋值中的所有属性。与逐场复制相比,速度提高了50%以上,每次使用MemberwiseClone创建新对象的速度提高了约60%。

2 个答案:

答案 0 :(得分:3)

如果您的类不可继承,您可以将类的整个状态存储在公开字段结构中(公开字段,而不是属性!)。这将要求您使用结构名称为所有字段名称添加前缀,这在源代码中是丑陋的,但访问该结构中的字段将与访问直接放置在封闭类中的字段一样快。但是,这样做可以让您使用简单的赋值语句将结构从一个对象实例复制到另一个对象实例。

答案 1 :(得分:2)

一般来说,你不能比按字段复制字段更快。

这一般。有一个例外:如果你的类是blittable(因此不包含对非blittable类的任何引用)并且它是用[StructLayout(LayoutKind.Sequential)](或[StructLayout(LayoutKind.Explicit)]声明的,但它更复杂测试)然后你可以把它钉住

GCHandle handle = GCHandle.Alloc(refToYourClass, GCHandleType.Pinned);

从那里开始一个新的世界......你可以直接逐字节地复制你的班级内容,其中一些不安全(键盘为unsafe不安全为“用剪刀“代码”。

但为了这个工作,你的班级必须是快乐的!

最快的方法是UnsafeCopy8,它复制8个字节的块(32位和64位)。最快的PInvoke方法是memcpy

[StructLayout(LayoutKind.Sequential)]
class MyClass
{
    public int A { get; set; }
    public int B { get; set; }
    public int C { get; set; }
    public int D { get; set; }
    public int E { get; set; }
    public int F { get; set; }
    public int G { get; set; }
    public byte H { get; set; }
}

class Program
{
    [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
    static extern IntPtr memcpy(IntPtr dest, IntPtr src, UIntPtr count);

    [DllImport("kernel32.dll", SetLastError = false)]
    static extern void CopyMemory(IntPtr dest, IntPtr src, UIntPtr count);

    static void Main()
    {
        MyClass mc = new MyClass { A = 1, B = 2, C = 3, D = 4, E = 5, F = 6, G = 7, H = 8 };
        MyClass mc2 = new MyClass();

        int size = Marshal.SizeOf(typeof(MyClass));

        var gc = GCHandle.Alloc(mc, GCHandleType.Pinned);
        var gc2 = GCHandle.Alloc(mc2, GCHandleType.Pinned);

        IntPtr dest = gc2.AddrOfPinnedObject();
        IntPtr src = gc.AddrOfPinnedObject();

        // Pre-caching
        memcpy(dest, src, (UIntPtr)size);
        CopyMemory(dest, src, (UIntPtr)size);
        UnsafeCopy(dest, src, size);
        UnsafeCopy8(dest, src, size);

        int cycles = 10000000;

        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;

        var sw1 = Stopwatch.StartNew();

        for (int i = 0; i < cycles; i++)
        {
            memcpy(dest, src, (UIntPtr)size);
        }

        sw1.Stop();

        var sw2 = Stopwatch.StartNew();

        for (int i = 0; i < cycles; i++)
        {
            CopyMemory(dest, src, (UIntPtr)size);
        }

        sw2.Stop();

        var sw3 = Stopwatch.StartNew();

        for (int i = 0; i < cycles; i++)
        {
            UnsafeCopy(dest, src, size);
        }

        sw3.Stop();

        var sw4 = Stopwatch.StartNew();

        for (int i = 0; i < cycles; i++)
        {
            UnsafeCopy8(dest, src, size);
        }

        sw4.Stop();

        gc.Free();
        gc2.Free();

        Console.WriteLine("IntPtr.Size: {0}", IntPtr.Size);
        Console.WriteLine("memcpy:      {0}", sw1.ElapsedTicks);
        Console.WriteLine("CopyMemory:  {0}", sw2.ElapsedTicks);
        Console.WriteLine("UnsafeCopy:  {0}", sw3.ElapsedTicks);
        Console.WriteLine("UnsafeCopy8: {0}", sw4.ElapsedTicks);
        Console.ReadKey();
    }

    static unsafe void UnsafeCopy(IntPtr dest, IntPtr src, int size)
    {
        while (size >= sizeof(int))
        {
            *((int*)dest) = *((int*)src);

            dest = (IntPtr)(((byte*)dest) + sizeof(int));
            src = (IntPtr)(((byte*)src) + sizeof(int));
            size -= sizeof(int);
        }

        if (size >= sizeof(short))
        {
            *((short*)dest) = *((short*)src);

            dest = (IntPtr)(((byte*)dest) + sizeof(short));
            src = (IntPtr)(((byte*)src) + sizeof(short));
            size -= sizeof(short);
        }

        if (size == sizeof(byte))
        {
            *((byte*)dest) = *((byte*)src);
        }
    }

    static unsafe void UnsafeCopy8(IntPtr dest, IntPtr src, int size)
    {
        while (size >= sizeof(long))
        {
            *((long*)dest) = *((long*)src);

            dest = (IntPtr)(((byte*)dest) + sizeof(long));
            src = (IntPtr)(((byte*)src) + sizeof(long));
            size -= sizeof(long);
        }

        if (size >= sizeof(int))
        {
            *((int*)dest) = *((int*)src);

            dest = (IntPtr)(((byte*)dest) + sizeof(int));
            src = (IntPtr)(((byte*)src) + sizeof(int));
            size -= sizeof(int);
        }

        if (size >= sizeof(short))
        {
            *((short*)dest) = *((short*)src);

            dest = (IntPtr)(((byte*)dest) + sizeof(short));
            src = (IntPtr)(((byte*)src) + sizeof(short));
            size -= sizeof(short);
        }

        if (size == sizeof(byte))
        {
            *((byte*)dest) = *((byte*)src);
        }
    }
}