30

I'm trying to improve the performance of our application. We have a lot of Activator.CreateInstance calls that are causing some grief.

We instantiate a lot of classes based on an interface (ITabDocument) and after looking around I thought of using this code:

The code is no better (infact marginally slower) than using the Activator.CreateInstance code we had.

    public static Func<T> CreateInstance<T>(Type objType) where T : class, new()
    {
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, null, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes));
        ilGen.Emit(OpCodes.Ret);
        return (Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
    }

I'm wondering why this is, all I'm doing is:

ITabDocument document = CreateInstance<ITabDocument>(Type.GetType("[Company].Something"));

Is there a better way of creating objects which would assist with the above? Its a little hard when you're not sure of the concrete type.


  • How often are you calling CreateInstance - because the whole point of that is that you'd call CreateInstance once but then remember the factory delegate. If you're calling CreateInstance on each operation, then yes, it'll be slower... - Jon Skeet
  • Some design patterns may help you, like factory method, abstract factory and similar creational patterns. They allow you to late bind your objects. You could look here: oodesign.com - Nickolodeon
  • @Jon Skeet Thanks for replying, I'm calling it at least 20-30 times as we need to create that many tabbed documents at start and they're all different implementations of ITabbedDocument. - Tiffany Townsend
  • @Tiffany: But are they all different types? That's the important thing - you should only call that method once per type. - Jon Skeet
  • Oops, yes they are all different types. I've edited my example - Tiffany Townsend

5 답변


42

I did some benchmarking between these (I would write down the bare minimum details):

public static T Instance() //~1800 ms
{
    return new T();
}

public static T Instance() //~1800 ms
{
    return new Activator.CreateInstance<T>();
}

public static readonly Func<T> Instance = () => new T(); //~1800 ms

public static readonly Func<T> Instance = () => 
                                 Activator.CreateInstance<T>(); //~1800 ms

//works for types with no default constructor as well
public static readonly Func<T> Instance = () => 
               (T)FormatterServices.GetUninitializedObject(typeof(T)); //~2000 ms


public static readonly Func<T> Instance = 
     Expression.Lambda<Func<T>>(Expression.New(typeof(T))).Compile();  
     //~50 ms for classes and ~100 ms for structs

As CD says compiled expression is the fastest, and by a big margin. All the methods except (T)FormatterServices.GetUninitializedObject(typeof(T)) work only for types with default constructor.

And caching the compiled resultant delegate is trivial when you have a static class per generic type. Like:

public static class New<T> where T : new()
{
    public static readonly Func<T> Instance = Expression.Lambda<Func<T>>
                                              (
                                               Expression.New(typeof(T))
                                              ).Compile();
}

Note the new constraint. Call anything

MyType me = New<MyType>.Instance();

Except for the first time the class is being loaded in memory, the execution is going to be fastest.

To have a class that handles both types with default constructor and without, I took a hybrid approach, from here:

public static class New<T>
{
    public static readonly Func<T> Instance = Creator();

    static Func<T> Creator()
    {
        Type t = typeof(T);
        if (t == typeof(string))
            return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

        if (t.HasDefaultConstructor())
            return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

        return () => (T)FormatterServices.GetUninitializedObject(t);
    }
}

public static bool HasDefaultConstructor(this Type t)
{
    return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}

Will handle value types too in an efficient manner.

Note that (T)FormatterServices.GetUninitializedObject(t) will fail for string. Hence special handling for string is in place to return empty string.


  • And how would this apply to creating an object whose type is only known at runtime as per the question? The question's use of generics is only for casting the returned object to a base class. In the example you have provided, the type of the instance returned is the same as the generic parameter and thus a normal constructor could be used (excepting the fact that it can also create an uninitialized object). - Richard Collette
  • @RichardCollette I agree its not directly answering the question but shows a way to have nice little helper class (the class is incomplete, hopefully I will update it a day). Nonetheless, it isn't too difficult to fine tune that to workable solution for OP (just use provided type instead of typeof(T)). - nawfal
  • The first one, the simple generic, new T()... It cannot be correct. This cannot be slower than the lambda version at the end. This should be as fast as a non-generic constructor, as generics are handled at compile time. Can you double check it? It's important, because it places doubt on all results in your otherwise excellent post! - Timo
  • @Timo depends on what you mean by "generics are handled at compile time". Some static checking is done, sure. But T will always have the runtime type. I think compiler should be smart enough to not rely on reflection in generic case but unfortunately it does. That's the whole point. You can also confirm this by inspecting IL. - nawfal
  • Read this blog codeblog.jonskeet.uk/2011/08/22/… from Jon Skeet. And also the various benchmarks by users in comments all which verifies my results. Also note my comment in that blog which highlights how behaviour changed in Roslyn which is even slower now. - nawfal

15

This might help: Don’t use Activator.CreateInstance or ConstructorInfo.Invoke, use compiled lambda expressions:

// Make a NewExpression that calls the ctor with the args we just created
NewExpression newExp = Expression.New(ctor, argsExp);                  

// Create a lambda with the New expression as body and our param object[] as arg
LambdaExpression lambda = Expression.Lambda(typeof(ObjectActivator), newExp, param);            


// Compile it
ObjectActivator compiled = (ObjectActivator)lambda.Compile();


  • Thanks, I saw this earlier but wasn't sure whether I can call it adhoc like I'd need. - Tiffany Townsend
  • Thanks for the link, it leads onto this blog rogeralsing.com/2008/02/28/linq-expressions-creating-objects which I've been reading and I think it may now be one of the best blogs I've ever seen. Thank you so much - RichK
  • Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference. Please edit your answer to correct this, then flag it as "in need of moderator intervention" and request un-deletion. - Matt

7

The problem is if your are going to call CreateInstance over and over again directly rather than saving the result somewhere and using that result over and over again, you should probably just go ahead and do you caching inside of it.

internal static class DelegateStore<T> {
     internal static IDictionary<string, Func<T>> Store = new ConcurrentDictionary<string,Func<T>>();
}

public static T CreateInstance<T>(Type objType) where T : class
{
    Func<T> returnFunc;
    if(!DelegateStore<T>.Store.TryGetValue(objType.FullName, out returnFunc)) {
        var dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + objType.Name, objType, null, objType);
        ILGenerator ilGen = dynMethod.GetILGenerator();
        ilGen.Emit(OpCodes.Newobj, objType.GetConstructor(Type.EmptyTypes));
        ilGen.Emit(OpCodes.Ret);
        returnFunc = (Func<T>)dynMethod.CreateDelegate(typeof(Func<T>));
        DelegateStore<T>.Store[objType.FullName] = returnFunc;
    }
    return returnFunc();
}


  • You actually don't need a dictionary in your DelegateStore<T> class at all because that generic class automatically gets a unique per-T instantiation already. This is true even though it is a static class. So all you need in DelegateStore<T> is simply internal static Func<T> _cached_func; As you have it, you're creating many dictionaries--a new one for every T--each containing just one single cached delegate. - Glenn Slayden

3

You're probably getting some overhead from the generation of the same code.

The ILGenerator dynamically creates code for the factory.

Create somekind of map or Dictionary of types you've already used, and keep the factory method created for that type.


0

Generic method for constructing delegates, calling constructor directly. Automatically searches constructor in given type with signature of given delegate type and constructs delegate of that type. Code here:

/// <summary>
/// Reflective object construction helper.
/// All methods are thread safe.
/// </summary>
public static class Constructor
{
    /// <summary>
    /// Searches an instanceType constructor with delegateType-matching signature and constructs delegate of delegateType creating new instance of instanceType.
    /// Instance is casted to delegateTypes's return type. 
    /// Delegate's return type must be assignable from instanceType.
    /// </summary>
    /// <param name="delegateType">Type of delegate, with constructor-corresponding signature to be constructed.</param>
    /// <param name="instanceType">Type of instance to be constructed.</param>
    /// <returns>Delegate of delegateType wich constructs instance of instanceType by calling corresponding instanceType constructor.</returns>
    public static Delegate Compile(Type delegateType,Type instanceType)
    {
        if (!typeof(Delegate).IsAssignableFrom(delegateType))
        {
            throw new ArgumentException(String.Format("{0} is not a Delegate type.",delegateType.FullName),"delegateType");
        }
        var invoke = delegateType.GetMethod("Invoke");
        var parameterTypes = invoke.GetParameters().Select(pi => pi.ParameterType).ToArray();
        var resultType = invoke.ReturnType;
        if(!resultType.IsAssignableFrom(instanceType))
        {
            throw new ArgumentException(String.Format("Delegate's return type ({0}) is not assignable from {1}.",resultType.FullName,instanceType.FullName));
        }
        var ctor = instanceType.GetConstructor(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null);
        if(ctor == null)
        {
            throw new ArgumentException("Can't find constructor with delegate's signature","instanceType");
        }
        var parapeters = parameterTypes.Select(Expression.Parameter).ToArray();

        var newExpression = Expression.Lambda(delegateType,
            Expression.Convert(Expression.New(ctor, parapeters), resultType),
            parapeters);
        var @delegate = newExpression.Compile();
        return @delegate;
    }
    public static TDelegate Compile<TDelegate>(Type instanceType)
    {
        return (TDelegate) (object) Compile(typeof (TDelegate), instanceType);
    }
}

is part of Yappi project's sources. Using it you can construct delegate calling any constructor of given type, including constructor with parameters (except ref and out parameters).

Sample usage:

var newList = Constructor.Compile<Func<int, IList<String>>>(typeof (List<String>));
var list = newList(100);

After construction of delegate, store it somewhere in static dictionary or in static field of class with generic parameter. Don't construct new delegate each time. Use one delegate for constructing multiple instances of given type.

Linked


Related

Latest