ICloneable - Revisited

Published 07 June 07 10:19 PM | andersnoras 

[Update: I copied the timing results from a test run that didn't do deep cloning for the LCG optimized cloning algorithm. This made it appear that LCG was faster than hand written code - which of course is ludicrous. I kind of wonder why no one noticed this.]

Jonas Follesø has a blog post on the performance differences between handwritten IClonable and reflection based ICloneable implementations. The example he uses is from a project I'm quite familiar with so I just had to blog about this.

The project he referes to uses serialization based cloning to a great extent. This is far from a performant way to clone objects, but its a very convenient way to do it. When developers don't have to write or maintain cloning code there is less work, less code and less chance of a bug being introduced when someone forgets to update the Clone method after adding a field.
Alternatives to the super easy serialization trick are reflection based solutions. Jonas referes to a naïve generic fieldwise deep clone algorithm in his post and concludes that the performance of hand-written code is superior to reflection. This shouldn't be a surprise to anyone - reflection has its costs.

public object Clone()
{
    ImplementedCloneOrder[] clonedOrders=new ImplementedCloneOrder[Orders.Length];
    for (int i=0;i<Orders.Length;i++)
    {
        clonedOrders[i] = (ImplementedCloneOrder) ((ImplementedCloneOrder)Orders[i]).Clone();
    }
    return
        new ImplementedCloneCustomer(CustomerID, CompanyName, Address, City, Region, PostalCode, Country, Phone, Fax,
        clonedOrders);
}

Another way to do reflection is to use bytecode optimized reflection. NHibernate does this to crank up the speed of its reflection operations. On .NET 1.x you can use the CodeDom to do this, while you're better of using LCG in .NET 2.0. So how does this compare to the other techniques? Below you can see the performance stats from cloning 100000 simple object graphs using different means.

Implemented Clone() method: Time: 128 ms, 458250 ticks (100000 operations)
Naïve reflection Clone() method: Time: 12566 ms, 44983213 ticks (100000 operations)
Optmized reflection Clone() method: Time: 866 ms, 239321 ticks (100000 operations)
Serialized Clone() method: Time: 25541 ms, 91425385 ticks (100000 operations)

As you can see the hardcoded Clone method is the fastest at only 128 ms, while optimized reflection takes a total of 866 ms to finish which still is rather fast.

With optimized reflection you can have the best of both worlds; great performance and easy to maintain code.

I'm in a bit of an hurry now, so you'll have to wait until tomorrow for the full code. Stay tuned!

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Jonas Follesø said on June 9, 2007 4:02 AM:

Hi Anders!

Cool that you picked up on my post. The goal of the post wasn't to prove that hand written cloning code is better than reflection. As you say in your post, this shouldn't be a surprise to anyone as reflection has it's cost.

The point of the post was to cover a real world problem where reflection had gotten them into problems. The reason for this was that the guy who wroth the calculation engine didn't know/had forgotten that he was taking a dependency on a class using reflection. Another key point of the post is how powerful ANTS can be in situations like this. The developer I helped out hadn't used ANTS before, and I'm quite sure he will in the future.

Anyhow, thanks for picking up on the post. Looking forward to the example code on bytecode optimized reflection and cloning. I'll link back to you when you've posted the sample.

Btw: Didn't know about your blog until Google Alerts dropped me an email saying my name was mentioned on your page. Just read through your last 15 points. Lots of interesting reading, specially on Java 1.5 and DSL's and your Java LINQ demo. Good luck with JavaZone!

Leave a Comment

(required) 
(optional)
(required) 
Enter the code you see below