Performance of Dynamic Object Instantiation

Posted on August 14, 2005  |  

Posted in Development

11 comments

As Provider pattern has been gaining more and more favor among developers, I’ve been wondering about performance implications of dynamic object (provider) instantiation it mandates. The most common mechanism to create providers on the fly has been via Activator.CreateInstance. Steve Maine did a nice perf test of Activator vs. the new operator, which showed that Activator was quite slow (which was reasonable to expect).

In addition to Steve’s tests, I thought of one more way to instantiate an object dynamically: to cache its constructor and then invoke it repeatedly to give life to new objects.

Suppose Target is some simple class. We can get to its constructor (parameterless or not), call Invoke and cast it to Target.

Type type = typeof (Target);
ConstructorInfo constructor = type.GetConstructor (new Type[] {});
Target t = (Target) constructor.Invoke (null);

I use this approach on my site, except that I cache the constructor and only call Invoke every time.

public static IDataProvider Instance ()
{
 Cache cache = HttpRuntime.Cache;
 if (null == cache ["DataProvider"]) 
 {
  ...
  Type type = Type.GetType (providerTypeName);
  cache.Insert ("DataProvider", type.GetConstructor (new Type[] {}));
  ...
 }
 return (IDataProvider)(((ConstructorInfo) 
         cache["DataProvider"]).Invoke (null));
}

To see how this approach faired against Activator.CreateInstance I put together a console test app to measure instantiation time of 1,000,000 objects via new, Activator.CreateInstance and constructor caching. I took a shortcut and adapted Eric Gunnerson’s test app he created to measure performance of dynamic code invocation.

Test Duration
InstantiateDirect 0.02303477 sec
InstantiateWithActivatorWithGetType 1.66457 sec
InstantiateWithActivatorWithGetTypePrefetched 1.661113 sec
InstantiateWithCachedConstructor 1.237231 sec

I used a complimentary copy of Rich Chart Builder to create a chart with the same test results:

Results of dynamic object instantiation benchmark

Creating 1,000,000 instances of a simple class directly took only 0.023 sec, while other types of instantiation were significantly heavier. Prefetching the constructor helped a little bit and beat Activator. This is pretty much what I expected, but it was interesting to see the tally nonetheless.

If you want to poke under the hood, feel free to grab source code.

11 comments

Jeff Gonzalez
on August 18, 2005

I wonder if you implemented ICloneable on the type you would be dynamically creating and cached the default created instance instead if you would get better performance.

I suppose it wouldn't be as dynamic, and I suppose it depends on the requirements of your application. Do you typically have several data providers from a single application?

If not I think you could create the type with Activator.CreateInstance at appstart, clone it immediately, store it in cache and then just clone the cached instance.


Jeff Gonzalez
on August 18, 2005

So I did a quick test...

I implemented ICloneable on your Target class

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

Then I added the following code above your result loop:

Type type = typeof (Target);
Target t = (Target) Activator.CreateInstance (type);
Hashtable cachedTypes = new Hashtable();
cachedTypes.Add("Target", t);

This code inside the for loop:
Target realType = ((ICloneable)cachedTypes["Target"]).Clone() as Target;

These are the results I got, my additional test is called 'InstantiateWithCachedActivatorWithGetType' :

InstantiateDirect: 0.03551513
InstantiateWithActivatorWithGetType: 0.8604999
InstantiateWithActivatorWithGetTypePrefetched: 0.8212122
InstantiateWithCachedConstructor: 0.9590532
InstantiateWithCachedActivatorWithGetType: 0.2592056

Not quite as fast as direct instantiation, but I think the performance gain was pretty good. Let me know what you think.


Milan Negovan
on August 19, 2005

I think you're on to something interesting. ;) I need to read up on how shallow and deep cloning works because I'm a bit rusty on this.


Dave Bacher
on July 26, 2006

Cache a delegate, instead of the constructor.

[Provider("name")]
class myProviderFactory
{
public static baseProvider CreateInstance()
{
return (baseProvider) new myProvider();
}
}

public delegate baseProvider CreateInstanceDelegate();

...

MethodInfo mi = factory.GetMethod("CreateInstance", BindingFlags.Static);
CreateInstanceDelegate cid = System.Delegate.CreateDelegate(CreateInstanceDelegate, mi);
return cid();

...



Part of the issue with Invoke is that it still has to perform reflection in order to perform its call.

Another fast mechanism is:
System.AppDomain.CurrentDomain.CreateInstance("name", false);
(doing it by the System.Type is even faster)

This removes the need to cache at all, if you can handle a 1 to 1 correlation between provider names and your class.


KVH
on October 1, 2007

.Net 2.0 Dual Core 3.19GHz

InstantiateDirect: 0,02239186
InstantiateWithActivatorWithGetType: 0,2616625
InstantiateWithActivatorWithGetTypePrefetched: 0,2451015
InstantiateWithCachedConstructor: 1,805805
InstantiateWithCurrentDomainCreateInstanceAndUnwrap: 67,52738

From current domain is very slow. (67,52738)
Target t = (Target)(AppDomain.CurrentDomain.CreateInstance(type.Assembly.FullName, type.FullName)).Unwrap();


Philip
on October 1, 2007

Reflector shows that AppDomain.CurrentDomain.CreateInstance still uses the System.Activator. Not sure why it's so much slower than the other Activator benchmarks.

Phil


Pete
on November 27, 2007

use a DynamicMethod to generate a cached delegate (by type), then call that delegate upon each instantiation of a particular class. It's as close as I could get to a pure call performance (~+15%) which ain't bad at all.

http://tinyurl.com/339p9k


Philip
on November 28, 2007

A very fast way, almost as fast as direct, is to use System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type As System.Type).

I did a write-up on it here: http://www.codeproject.com/useritems/DynamicFactoryDemo.asp


Matt Dockerty
on November 30, 2007

Thanks for this article. After reading it, I've rolled my own version which uses caching and direct instantiation. I've posted it below in case it's any help.

Presuming you have a handful of potential objects in your application and you don't mind the requirement of an empty constructor, this solution should be the quickest overall. It calls Activator.CreateInstance on first request and then directly instantiates objects thereafter. You can also prepare the cache ahead of time in a less performance-critical section of your code such as on startup.

public static class CachedActivator {
static CachedActivator() {
genericFactoryType = typeof(GenericFactory);
}

public static void PrepareCache(Type t) {
if(!cachedFactories.ContainsKey(t)) {
cachedFactories.Add(t, new Factory(t));
}
}

public static object CreateInstance(Type t) {
PrepareCache(t);
return cachedFactories[t].Create();
}

#region Embedded Classes

private class Factory {
public Factory(Type t) {
Type initialisedFactoryType =
genericFactoryType.MakeGenericType(t);

this.genericFactory =
(GenericFactoryBase)Activator.CreateInstance(initialisedFactoryType);
}

public object Create() {
return this.genericFactory.CreateObject();
}

private GenericFactoryBase genericFactory;
}

private abstract class GenericFactoryBase {
public abstract object CreateObject();
}

private class GenericFactory : GenericFactoryBase where T : class, new() {
public override object CreateObject() {
return this.Create();
}

public T Create() {
return new T();
}
}

#endregion

#region Private Members

private static Type genericFactoryType;
private static Dictionary cachedFactories =
new Dictionary();

#endregion
}


Matt Dockerty
on November 30, 2007

Lol, your blog ate my generics! Imagine a (T) in the right places and you won't go far wrong.


Norman
on October 27, 2010

A few years later, but what needs to change for the Matt Dockerty's code snippet to work? It is late over here and I am drawing a silly blank.