DeepClone(使用二进制序列化)的性能与手动设置属性的比较

发布于 2025-01-08 02:28:32 字数 813 浏览 1 评论 0原文

我有一个对象集合,我正在尝试克隆该集合并尝试了解不同方法的性能影响。

集合中的对象有大约 20 个属性,所有字符串、整数、浮点数(该对象内部没有任何嵌套对象)。这两种方法是:

  1. 创建 DeepClone() 方法:

    公共静态类ExtensionMethods
    {
        公共静态 T DeepClone(此 T a)
        {
           使用(var流=新的MemoryStream())
           {
               var 格式化程序 = new BinaryFormatter();
               格式化程序.Serialize(流, a);
               流.Position = 0;
              返回(T)格式化程序。反序列化(流);
           }
       }
    

    }

  2. 手动编写“复制”代码,其中我循环遍历集合并“新建”一个新对象,然后手动设置所有 20 个属性。像这样的东西

     public MyObject 复制(MyObject myObj)
    {
     var obj = new MyObject();
     obj.Prop1 = myObj.Prop1;
     obj.Prop2 = myObj.Prop2;
     返回对象;
    

    }

,所以我想得到人们的反馈:

  1. 一个应该比另一个快得多吗?我本想选择二,但我的测试似乎不支持这一点,所以我试图弄清楚我是否做错了什么。

  2. 有什么方法可以更快地做到这一点吗?

I have a collection of objects and I am trying to clone this collection and trying to understand performance implication of different approaches.

The object in the collection has about 20 properties all strings, ints, floats (this objects doesn't have any nested objects inside of it). The two approaches are:

  1. Create DeepClone() method:

    public static class ExtensionMethods
    {
        public static T DeepClone<T>(this T a)
        {
           using (var stream = new MemoryStream())
           {
               var formatter = new BinaryFormatter();
               formatter.Serialize(stream, a);
               stream.Position = 0;
              return (T)formatter.Deserialize(stream);
           }
       }
    

    }

  2. Manually write "copy" code where i am looping through the collection and "new"ing a new object and then manually setting all of the 20 properties. something like this

     public MyObject Copy(MyObject myObj)
    {
     var obj = new MyObject();
     obj.Prop1 = myObj.Prop1;
     obj.Prop2 = myObj.Prop2;
     return obj;
    

    }

I am getting very inconsistent results so I wanted to get peoples feedback on:

  1. Should one be much faster that the other? I would have thought choice two but my tests don't seem to support this so I am trying to figure out if I am doing something wrong.

  2. Is there any way to do this even faster?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

新一帅帅 2025-01-15 02:28:32

好吧,首先 BinaryFormatter 路由肯定会更慢,因为它使用反射来获取/设置属性。最常见的方法是将 IClonable 接口与复制构造函数结合使用。

class A : ICloneable
{
    private readonly int _member;

    public A(int member)
    {
        _member = member;
    }

    public A(A a)
    {
        _member = a._member;
    }

    public object Clone()
    {
        return new A(this);
    }
}

当然严格来说你只需要复制构造函数,这应该是最快的方法。如果您的对象很简单,您应该尝试使用内置的 MemberwiseClone 函数。

class A : ICloneable
{
    private readonly int _member;

    public A(int member)
    {
        _member = member;
    }

    public object Clone()
    {
        return MemberwiseClone();
    }
}

同时,我编写了一些测试代码来查看 MemberwiseClone() 是否比使用复制构造函数更快或更慢。您可以在此处找到它。我发现 MemberwiseClone 实际上比 CopyConstructor 慢得多,至少在小类上是这样。请注意,使用 BinaryFormatter 的速度非常慢。

Well, first of all BinaryFormatter route must definitely be slower, since it uses reflection to get/set properties. The most common method is using the IClonable interface in conjunction with a copy constructor.

class A : ICloneable
{
    private readonly int _member;

    public A(int member)
    {
        _member = member;
    }

    public A(A a)
    {
        _member = a._member;
    }

    public object Clone()
    {
        return new A(this);
    }
}

Of course strictly speaking you only need the copy constructor, which should be the fastest method. If your objects are simple, you should try using in-built MemberwiseClone function.

class A : ICloneable
{
    private readonly int _member;

    public A(int member)
    {
        _member = member;
    }

    public object Clone()
    {
        return MemberwiseClone();
    }
}

Meanwhile, I wrote some test code to see if MemberwiseClone() was severely faster or slower than using a copy constructor. You can find it here. I found that MemberwiseClone is actually much slower than doing a CopyConstructor, at least on small classes. Note that using the BinaryFormatter is insanely slow.

疑心病 2025-01-15 02:28:32

在我之前的角色中,我们调查了这个问题,因为我们正在缓存对象并希望在从缓存中分发它们之前克隆它们。

我们做了一些详细的基准测试,发现属性设置总是比 BinaryFormatter 方法至少快一个数量级,尽管显然需要手动实现而不是更简单的 BinaryFormatter 方法。对于深度对象图,差异变得更加明显 IIRC。

最后,我们决定采用三管齐下的方法来“克隆”:

  • 如果类型是不可变的(我们将使用标记接口 IImutable 来表示,但您同样可以使用属性或类似的东西) ,我们将通过返回原始实例来“克隆”。由于我们知道没有人可以改变它,因此继续返回相同的实例是安全的。显然,这是最快的“克隆”类型,尽管显然根本不是真正的克隆。
  • 如果该类型实现了我们自己的 IDeepCloneable 接口(类似于您的第二个示例 - 但通用),我们将使用它。 [此通用接口将从非通用等效 IDeepCloneable 继承]
  • 如果做不到这一点,我们将退回到您的第一个示例,BinaryFormatter

我提到“不可变”方法是因为根据您正在做的事情,有时最好的方法是重新设计您需要克隆的类,这样它们就根本不需要克隆。如果它们在创建后本质上是只读的,那么这很容易;但即使不是,有时构建器/不可变类型方法也是有用的(请参阅框架中的 UriUriBuilder 。前者本质上是不可变的,而后者可以是用于构建和/或改变前者的实例)。

In my previous role, we investigated this very issue, as we were caching objects and wanted to clone them before handing them out from the cache.

We did some detailed benchmarking and found that property setting was always at least an order of magnitude faster then the BinaryFormatter approach, although obviously required a hand-rolled implementation as opposed to the much simpler BinaryFormatter approach. For deep object graphs, the difference became more pronounced IIRC.

In the end, we settled on a three pronged approach to "cloning":

  • if the type was immutable (which we would signify with a marker interface, IImutable, but you could equally use an attribute or somesuch), we would "clone" by returning the original instance. Since we knew no-one could mutate it, it was safe to keep returning the same instance. Obviously this was the fastest type of "clone", although clearly not really a clone at all.
  • If the type implemented our own IDeepCloneable<T> interface (which would be like your second example - but generic) we'd use that. [This generic interface would inherit from a non-generic equivalent IDeepCloneable]
  • Failing that, we'd fall back to your first example, the BinaryFormatter.

I mention the "immutable" approach because depending on what you are doing, sometimes the best way is to redesign the classes you need to clone so they don't need to be cloned at all. If they are essentially read-only once created, this is easy; but even if not, sometimes the builder/immutable-type approach is useful (see Uri vs. UriBuilder in the framework. The former is essentially immutable, while the latter can be used to build and/or mutate instances of the former).

娜些时光,永不杰束 2025-01-15 02:28:32

为此使用序列化是一个非常聪明的想法。然而,一一设置属性应该同样快,并且大多数时候(除非对象非常小)必须更快 - 您所做的工作更少,并且不使用反射。

您能否重现使用序列化比“手动”复制属性更快的情况?

Using serialization for that is a very clever idea. However, setting the properties one by one should be just as fast, and most of the time (except when the objects are really small) must faster - you're doing less work, and not using reflection.

Can you reproduce a case where using serialization was faster than copying the properties 'by hand'?

情深缘浅 2025-01-15 02:28:32

序列化肯定比属性分配慢。

二进制序列化是更好的选择,因为代码简单且易于维护。

相反,属性分配更好,因为可能在克隆期间,您不想克隆对象的所有属性,但希望能够顺便序列化它们。简而言之,您可以更好地控制流程。

Serialization is be definitely slower then just property assignment.

Binary serialization is preferable, cause the code is easy and easy to maintain.

The property assignment, instead, is better cause may be during the clone, you don't want to clone all properties of the object, but want to be able to serialize them by the way. In short you have more a control on the flow.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文