Fast creation of objects instead of Activator.CreateInstance(type)
Asked Answered
A

6

39

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 (in fact 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? It's a little hard when you're not sure of the concrete type.

Alnico answered 5/7, 2011 at 11:56 Comment(9)
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...Oldfangled
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.comRouleau
@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.Alnico
@Tiffany: But are they all different types? That's the important thing - you should only call that method once per type.Oldfangled
Oops, yes they are all different types. I've edited my exampleAlnico
possible duplicate of Activator.CreateInstance Performance AlternativeSlifka
@JonSkeet could you explain to me what you mean by remembering the delegate? Which delegate here? Do you mean using Delegate.CreateDelegate on Activator.CreateInstance or just storing a Func<> which directly calls CreateInstance?Slifka
@nawfal: I mean just store a reference to the value returned by Delegate.CreateDelegate, so that you don't need to create a new DynamicMethod each time.Oldfangled
@JonSkeet Ah I see, you were talking about DynamicMethod. I thought there was a way to make use of Delegate.CreateDelegate and Activator.CreateInstance together.Slifka
S
55

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.

Slifka answered 23/4, 2013 at 6:52 Comment(6)
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).Hermia
@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)).Slifka
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!Ritaritardando
@Ritaritardando 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.Slifka
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.Slifka
@Slifka Wow, guess I spoke too soon! Thanks for the lesson and high-performance workaround. I salute you!Ritaritardando
L
22

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();
Lex answered 5/7, 2011 at 12:0 Comment(4)
Thanks, I saw this earlier but wasn't sure whether I can call it adhoc like I'd need.Alnico
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 muchScoliosis
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.Kerrill
This is at least an order of magnitude faster, well worth it!Tannenberg
S
9

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();
}
Schwinn answered 11/7, 2011 at 13:28 Comment(2)
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.Yeeyegg
For anyone wanting to create value-types like this, the replacement for the Opcodes.Newobj line is: LocalBuilder localVar = ilGen.DeclareLocal(objType); ilGen.Emit(OpCodes.Ldloca_S, localVar); ilGen.Emit(OpCodes.Initobj, objType); ilGenerator.Emit(OpCodes.Ldloc_S, localVar); Thanks to SharpLab for helping me infer this!Tableware
B
7

UPDATED: Oct 13th, 2022
nawfal's answer benchmarked

Benchmarked in NET6.0, was just seeing how necessary this was still.

Added Activator.CreateInstance<T>(); test and struct tests.

Activator1 = new();
Activator2 = Activator.CreateInstance<T>();
Activator3 = New<T>.Instance();

TL;DR: Still recommended for simple classes. Don't use for structs.

using BenchmarkDotNet.Running;
using InstanceBenchmark;

//BenchmarkRunner.Run<ActivatorBenchmark<TestClass>>();
BenchmarkRunner.Run<ActivatorBenchmark<TestStruct>>();

public class TestClass
{
    public string Name { get; set; }
    public int Id { get; set; }
    public string Email { get; set; }
}

public struct TestStruct
{
    public string Name { get; set; }
    public int Id { get; set; }
    public string Email { get; set; }
}

[MemoryDiagnoser]
[SimpleJob(runtimeMoniker: RuntimeMoniker.Net60)]
[GenericTypeArguments(typeof(TestClass))]
[GenericTypeArguments(typeof(TestStruct))]
public class ActivatorBenchmark<T> where T : new()
{
    [Benchmark(Baseline = true)]
    [Arguments(1_000)]
    [Arguments(1_000_000)]
    [Arguments(100_000_000)]

    public void ActivatorTest1(int x)
    {
        for (int i = 0; i < x; i++)
        {
            var t = new T();
        }
    }

    [Benchmark]
    [Arguments(1_000)]
    [Arguments(1_000_000)]
    [Arguments(100_000_000)]

    public void ActivatorTest2(int x)
    {
        for (int i = 0; i < x; i++)
        {
            var t = Activator.CreateInstance<T>();
        }
    }

    [Benchmark]
    [Arguments(1_000)]
    [Arguments(1_000_000)]
    [Arguments(100_000_000)]
    public void ActivatorTest3(int x)
    {
        for (int i = 0; i < x; i++)
        {
            var t = New<T>.Instance();
        }
    }
}

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

        private 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;
    }
}

Class Results

// * Summary *

BenchmarkDotNet=v0.13.2, OS=Windows 11 (10.0.22000.1098/21H2)
Intel Core i9-10900KF CPU 3.70GHz, 1 CPU, 20 logical and 10 physical cores
.NET SDK=6.0.402
  [Host]   : .NET 6.0.10 (6.0.1022.47605), X64 RyuJIT AVX2
  .NET 6.0 : .NET 6.0.10 (6.0.1022.47605), X64 RyuJIT AVX2

Job=.NET 6.0  Runtime=.NET 6.0  

|         Method |         x |           Mean |         Error |        StdDev | Ratio | RatioSD |        Gen0 |     Allocated | Alloc Ratio |
|--------------- |---------- |---------------:|--------------:|--------------:|------:|--------:|------------:|--------------:|------------:|
| ActivatorTest1 |      1000 |       9.946 μs |     0.1927 μs |     0.2142 μs |  1.00 |    0.00 |      3.8147 |      39.06 KB |        1.00 |
| ActivatorTest2 |      1000 |       9.808 μs |     0.0721 μs |     0.0674 μs |  0.98 |    0.02 |      3.8147 |      39.06 KB |        1.00 |
| ActivatorTest3 |      1000 |       6.219 μs |     0.1199 μs |     0.1427 μs |  0.63 |    0.02 |      3.8223 |      39.06 KB |        1.00 |
|                |           |                |               |               |       |         |             |               |             |
| ActivatorTest1 |   1000000 |   9,834.625 μs |    31.8609 μs |    26.6053 μs |  1.00 |    0.00 |   3812.5000 |   39063.26 KB |        1.00 |
| ActivatorTest2 |   1000000 |  10,671.712 μs |    47.0675 μs |    44.0269 μs |  1.09 |    0.01 |   3812.5000 |   39063.26 KB |        1.00 |
| ActivatorTest3 |   1000000 |   6,295.779 μs |   121.9964 μs |   186.3014 μs |  0.65 |    0.03 |   3820.3125 |    39062.5 KB |        1.00 |
|                |           |                |               |               |       |         |             |               |             |
| ActivatorTest1 | 100000000 | 995,902.729 μs | 7,355.4492 μs | 6,520.4141 μs |  1.00 |    0.00 | 382000.0000 | 3906325.27 KB |        1.00 |
| ActivatorTest2 | 100000000 | 982,209.783 μs | 6,630.1000 μs | 5,176.3460 μs |  0.99 |    0.01 | 382000.0000 | 3906335.95 KB |        1.00 |
| ActivatorTest3 | 100000000 | 618,402.807 μs | 4,305.6817 μs | 4,027.5373 μs |  0.62 |    0.01 | 382000.0000 | 3906253.48 KB |        1.00 |

Struct Results

// * Summary *

BenchmarkDotNet=v0.13.2, OS=Windows 11 (10.0.22000.1098/21H2)
Intel Core i9-10900KF CPU 3.70GHz, 1 CPU, 20 logical and 10 physical cores
.NET SDK=6.0.402
  [Host]   : .NET 6.0.10 (6.0.1022.47605), X64 RyuJIT AVX2
  .NET 6.0 : .NET 6.0.10 (6.0.1022.47605), X64 RyuJIT AVX2

Job=.NET 6.0  Runtime=.NET 6.0

|         Method |         x |             Mean |         Error |        StdDev | Ratio | RatioSD | Allocated | Alloc Ratio |
|--------------- |---------- |-----------------:|--------------:|--------------:|------:|--------:|----------:|------------:|
| ActivatorTest1 |      1000 |         212.8 ns |       4.27 ns |       4.38 ns |  1.00 |    0.00 |         - |          NA |
| ActivatorTest2 |      1000 |         209.5 ns |       0.10 ns |       0.09 ns |  0.98 |    0.02 |         - |          NA |
| ActivatorTest3 |      1000 |       1,646.0 ns |       2.69 ns |       2.10 ns |  7.77 |    0.14 |         - |          NA |
|                |           |                  |               |               |       |         |           |             |
| ActivatorTest1 |   1000000 |     204,577.8 ns |     128.30 ns |     107.14 ns |  1.00 |    0.00 |         - |          NA |
| ActivatorTest2 |   1000000 |     204,569.4 ns |     116.38 ns |     108.86 ns |  1.00 |    0.00 |         - |          NA |
| ActivatorTest3 |   1000000 |   1,644,446.5 ns |  12,606.12 ns |   9,842.03 ns |  8.04 |    0.05 |       1 B |          NA |
|                |           |                  |               |               |       |         |           |             |
| ActivatorTest1 | 100000000 |  20,455,141.5 ns |  12,934.68 ns |  12,099.11 ns |  1.00 |    0.00 |      15 B |        1.00 |
| ActivatorTest2 | 100000000 |  20,460,807.6 ns |  25,571.37 ns |  19,964.44 ns |  1.00 |    0.00 |      15 B |        1.00 |
| ActivatorTest3 | 100000000 | 164,105,645.0 ns | 327,107.27 ns | 305,976.34 ns |  8.02 |    0.01 |     898 B |       59.87 |
Bambara answered 14/12, 2019 at 1:30 Comment(1)
seems things have hardly changed a bit =/Whitefly
G
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.

Gerstner answered 5/7, 2011 at 12:5 Comment(0)
R
1

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.

Rachellrachelle answered 30/7, 2011 at 11:41 Comment(1)
Why .editorconfig with spell checking is important: "parapeters" is comicotragic.Chargeable

© 2022 - 2024 — McMap. All rights reserved.