Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performance of DeepClone (using binary serialization) versus manually setting properties

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?

like image 408
leora Avatar asked Feb 23 '12 05:02

leora


People also ask

What is the most efficient way to deep clone an object in Javascript?

According to the benchmark test, the fastest way to deep clone an object in javascript is to use lodash deep clone function since Object. assign supports only shallow copy.

How can you actually the deep cloning of an object?

Deep Cloning We can do it by implementing a Cloneable interface and overriding the clone() method in every reference type we have in our object hierarchy. Then, we call super. clone() and these clone() methods in our object's clone method.

What is deep or shallow copy concept in C #?

This is called “Shallow Copy”. To get the same behavior for a Reference Type as well as a Value Type we use the Clone() method that belongs to the System. ICloneable interface. This is called a “Deep Copy”. We will see both behaviors in depth one by one.


2 Answers

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.

like image 122
Gleno Avatar answered Oct 29 '22 01:10

Gleno


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).

like image 43
Rob Levine Avatar answered Oct 28 '22 23:10

Rob Levine