Question

I am trying to optimize a piece of code that clones an object:

#region ICloneable
public object Clone()
{
    MemoryStream buffer = new MemoryStream();
    BinaryFormatter formatter = new BinaryFormatter();

    formatter.Serialize(buffer, this);     // takes 3.2 seconds
    buffer.Position = 0;
    return formatter.Deserialize(buffer);  // takes 2.1 seconds
}
#endregion

Pretty standard stuff. The problem is that the object is pretty beefy and it takes 5.4 seconds (according ANTS Profiler - I am sure there is the profiler overhead, but still).

Is there a better and faster way to clone?

Was it helpful?

Solution

  1. Don't implement ICloneable.

  2. The fast way to clone an object is to create a new instance of the same type and copy/clone all fields from the original instance to the new instance. Don't try to come up with a "generic" clone method that can clone any object of any class.

Example:

class Person
{
    private string firstname;
    private string lastname;
    private int age;

    public Person(string firstname, string lastname, int age)
    {
        this.firstname = firstname;
        this.lastname = lastname;
        this.age = age;
    }

    public Person Clone()
    {
        return new Person(this.firstname, this.lastname, this.age);
    }
}

OTHER TIPS

As I understand it, streams, even inner ones like this, are expensive.
Have you tried to just create a new object it and update the relevant fields to bring the object to the same state? I find it hard to believe your method takes less time.

That's a pretty expensive way to clone. The object never gets on the wire, so all the time doing serialisation is basically wasted. It will be way faster to do memberwise clone. I realise it's not an automagic solution, but it'll be the fastest.

Something along these lines:

class SuperDuperClassWithLotsAndLotsOfProperties {
  object Clone() {
    return new SuperDuperClassWithLotsAndLotsOfProperties {
      Property1 = Property1,
      Property2 = Property2,
    }

  public string Property1 {get;set;}
  public string Property2 {get;set;}
  }
}

Because manually copying fields is the fastest way I created a code generator, that reads your class definition and generates a clone method. All you need is the CGbR nuget package and a partial class that implements ICloneable. The generator will do the rest.

public partial class Root : ICloneable
{
    public Root(int number)
    {
        _number = number;
    }
    private int _number;

    public Partial[] Partials { get; set; }

    public IList<ulong> Numbers { get; set; }

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

    private Root()
    {
    }
} 

public partial class Root
{
    public Root Clone(bool deep)
    {
        var copy = new Root();
        // All value types can be simply copied
        copy._number = _number; 
        if (deep)
        {
            // In a deep clone the references are cloned 
            var tempPartials = new Partial[Partials.Length];
            for (var i = 0; i < Partials.Length; i++)
            {
                var value = Partials[i];
                value = value.Clone(true);
                tempPartials[i] = value;
            }
            copy.Partials = tempPartials;
            var tempNumbers = new List<ulong>(Numbers.Count);
            for (var i = 0; i < Numbers.Count; i++)
            {
                var value = Numbers[i];
                tempNumbers[i] = value;
            }
            copy.Numbers = tempNumbers;
        }
        else
        {
            // In a shallow clone only references are copied
            copy.Partials = Partials; 
            copy.Numbers = Numbers; 
        }
        return copy;
    }
}

And the partial class

public partial class Partial : ICloneable
{
    public short Id { get; set; }

    public string Name { get; set; }

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

public partial class Partial
{
    public Partial Clone(bool deep)
    {
        var copy = new Partial();
        // All value types can be simply copied
        copy.Id = Id; 
        copy.Name = Name; 
        return copy;
    }
}

Answer: There ARE better methods for cloning.

Reflection or Expression Trees are much faster then Serialization (reflection is 5x faster, expression trees are 20x faster).

enter image description here

If you use this linked cloning function as an extension method, each your cloning code shrinks to

#region ICloneable
public object Clone()
{
    return this.DeepCopyByExpressionTree();
}
#endregion

To use the extension method it is enough to have the file DeepCopyByExptressionTrees.cs anywhere in your solution.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top