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:
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);
}
}
}
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:
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.
Is there any way to do this even faster?
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.
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.
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.
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.
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":
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.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
]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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With