Does System.Activator.CreateInstance(T) have perfo

2019-01-13 20:31发布

Does System.Activator.CreateInstance(T) method have performance issues (since I'm suspecting it uses reflection) big enough to discourage us from using it casually?

5条回答
成全新的幸福
2楼-- · 2019-01-13 20:58

It depends on your use case. If you need very high performance and are creating many objects then using Activator.CreateInstance may be a problem.

But in most cases it will be fast enough and it is a very powerful method of creating objects.

In fact, most IoC Containers/Service locators/whatever you call them use this method to create an object of the type you are requesting.

If you are worried that the performance is not good enough then you should do a profiling of your application and measure if you have a bottleneck and where it is. My guess is that the call to Activator.CreateInstance will not be your problem.

查看更多
Summer. ? 凉城
3楼-- · 2019-01-13 21:00

Here's a sample C# .NET 4.0 program that tests:

  • Activator.CreateInstance
  • new T()
  • calling a delegate that calls new T()
  • generic new()
  • Activator.CreateInstance using a generic
  • Activator.CreateInstance using a generic and non-default bindings (e.g. to call internal constructor)

The output (timing values are in milliseconds from a 2014 beefy machine with x86 release build):

Test1 - Activator.CreateInstance<T>(): 8542
Test2 - new T() 1082
Test3 - Delegate 1214
Test4 - Generic new() 8759
Test5 - Generic activator 9166
Test6 - Generic activator with bindings 60772
Baseline 322

This is adopted from Lasse V. Karlsen's answer, but importantly includes generics. Note that specifying bindings slows down Activator using generics by more than a factor of 6!

using System;
using System.Reflection;
using System.Diagnostics;

namespace ConsoleApplication1
{
    public class TestClass
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            const int IterationCount = 100000000;

            // warmup
            Test1();
            Test2();
            Test3();
            Test4<TestClass>();
            Test5<TestClass>();
            Test6<TestClass>();

            // profile Activator.CreateInstance<T>()
            Stopwatch sw = Stopwatch.StartNew();
            for (int index = 0; index < IterationCount; index++)
                Test1();
            sw.Stop();
            Console.WriteLine("Test1 - Activator.CreateInstance<T>(): {0}", sw.ElapsedMilliseconds);

            // profile new T()
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                Test2();
            sw.Stop();
            Console.WriteLine("Test2 - new T() {0}", sw.ElapsedMilliseconds);

            // profile Delegate
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                Test3();
            sw.Stop();
            Console.WriteLine("Test3 - Delegate {0}", sw.ElapsedMilliseconds);

            // profile generic new()
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                Test4<TestClass>();
            sw.Stop();
            Console.WriteLine("Test4 - Generic new() {0}", sw.ElapsedMilliseconds);

            // generic Activator without bindings
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                Test5<TestClass>();
            sw.Stop();
            Console.WriteLine("Test5 - Generic activator {0}", sw.ElapsedMilliseconds);

            // profile Activator with bindings
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                Test6<TestClass>();
            sw.Stop();
            Console.WriteLine("Test6 - Generic activator with bindings {0}", sw.ElapsedMilliseconds);


            // profile Baseline
            sw.Restart();
            for (int index = 0; index < IterationCount; index++)
                TestBaseline();
            sw.Stop();
            Console.WriteLine("Baseline {0}", sw.ElapsedMilliseconds);
        }

        public static void Test1()
        {
            var obj = Activator.CreateInstance<TestClass>();
            GC.KeepAlive(obj);
        }

        public static void Test2()
        {
            var obj = new TestClass();
            GC.KeepAlive(obj);
        }

        static Func<TestClass> Create = delegate
        {
            return new TestClass();
        };

        public static void Test3()
        {
            var obj = Create();
            GC.KeepAlive(obj);
        }

        public static void Test4<T>() where T : new()
        {
            var obj = new T();
            GC.KeepAlive(obj);
        }

        public static void Test5<T>()
        {
            var obj = ((T)Activator.CreateInstance(typeof(T)));
            GC.KeepAlive(obj);
        }

        private const BindingFlags anyAccess = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

        public static void Test6<T>()
        {
            var obj = ((T)Activator.CreateInstance(typeof(T), anyAccess, null, null, null));
            GC.KeepAlive(obj);
        }

        static TestClass x = new TestClass();
        public static void TestBaseline()
        {
            GC.KeepAlive(x);
        }
    }
}
查看更多
够拽才男人
4楼-- · 2019-01-13 21:10

Yes, actually it has performance issue (in comparison with new()) since it uses Reflection and static compiler checks specially when you pass parameters to it (sending parameters to class's constructor) instead of using default constructor (like below)

//Too bad!!!
T someResult = (T)Activator.CreateInstance(
 typeof(T),   
 //parameter
 new object[] {id}
);

To use it or not in my opinion depends on two things:

First your application type and of course it's scale (and it's typical traffic)

And second (and more importantly) how and where you use Activator.CreateInstance method, for instance if you use it on every request with one or more constructor parameters (as I mentioned using with constructor parameters is almost one tenth slower than using without parameters (default constructor)), your application's performance deteriorate almost in a significant amount but for another instance if you use it once (on application_start for example) and with no constructor parameter it almost acts like new keyword

here is a detailed benchmark comparison between new(),Activator.CreateInstance and Type.GetInstance()

new vs Func vs Activator.CreateInstance()

查看更多
一夜七次
5楼-- · 2019-01-13 21:14

Yes there is a performance difference between calling

(MyClass)Activator.CreateInstance(typeof(MyClass));

and

new MyClass();

where the latter is faster. But determining whether the speed drop is big enough is up to your domain. In 90% of the case it's not an issue. Also note that for value types, Activator.CreateInstance is again slower because of the unboxing involved.

But here is the catch: For generic types, they are similar. new T() internally calls Activator.CreateInstance<T>() which in turn calls RuntimeType.CreateInstanceDefaultCtor(...). So if you're having a generic method to create new instance of T, then it shouldn't matter, though having a new() constraint and calling new T() is much more readable. Here's relevant link on the subject by Jon Skeet.

查看更多
Emotional °昔
6楼-- · 2019-01-13 21:16

As always, the only correct way to answer a question about performance is to actually measure the code.

Here's a sample LINQPad program that tests:

  • Activator.CreateInstance
  • new T()
  • calling a delegate that calls new T()

As always, take the performance program with a grain of salt, there might be bugs here that skews the results.

The output (timing values are in milliseconds):

Test1 - Activator.CreateInstance<T>() 
12342 

Test2 - new T() 
1119 

Test3 - Delegate 
1530 

Baseline 
578 

Note that the above timings are for 100.000.000 (100 million) constructions of the object. The overhead might not be a real problem for your program.

Cautionary conclusion would be that Activator.CreateInstance<T> is taking roughly 11 times as much time to do the same job as a new T() does, and a delegate takes roughly 1.5 times as much. Note that the constructor here does nothing, so I only tried to measure the overhead of the different methods.

Edit: I added a baseline call that does not construct the object, but does the rest of the things, and timed that as well. With that as a baseline, it looks like a delegate takes 75% more time than a simple new(), and the Activator.CreateInstance takes around 1100% more.

However, this is micro-optimization. If you really need to do this, and eek out the last ounce of performance of some time-critical code, I would either hand-code a delegate to use instead, or if that is not possible, ie. you need to provide the type at runtime, I would use Reflection.Emit to produce that delegate dynamically.

In any case, and here is my real answer:

If you have a performance problem, first measure to see where your bottleneck is. Yes, the above timings might indicate that Activator.CreateInstance has more overhead than a dynamically built delegate, but there might be much bigger fish to fry in your codebase before you get (or even have to get) to this level of optimization.

And just to make sure I actually answer your concrete question: No, I would not discourage use of Activator.CreateInstance. You should be aware that it uses reflection so that you know that if this tops your profiling lists of bottlenecks, then you might be able to do something about it, but the fact that it uses reflection does not mean it is the bottleneck.

The program:

void Main()
{
    const int IterationCount = 100000000;

    // warmup
    Test1();
    Test2();
    Test3();
    Test4();

    // profile Activator.CreateInstance<T>()
    Stopwatch sw = Stopwatch.StartNew();
    for (int index = 0; index < IterationCount; index++)
        Test1();
    sw.Stop();
    sw.ElapsedMilliseconds.Dump("Test1 - Activator.CreateInstance<T>()");

    // profile new T()
    sw.Restart();
    for (int index = 0; index < IterationCount; index++)
        Test2();
    sw.Stop();
    sw.ElapsedMilliseconds.Dump("Test2 - new T()");

    // profile Delegate
    sw.Restart();
    for (int index = 0; index < IterationCount; index++)
        Test3();
    sw.Stop();
    sw.ElapsedMilliseconds.Dump("Test3 - Delegate");

    // profile Baseline
    sw.Restart();
    for (int index = 0; index < IterationCount; index++)
        Test4();
    sw.Stop();
    sw.ElapsedMilliseconds.Dump("Baseline");
}

public void Test1()
{
    var obj = Activator.CreateInstance<TestClass>();
    GC.KeepAlive(obj);
}

public void Test2()
{
    var obj = new TestClass();
    GC.KeepAlive(obj);
}

static Func<TestClass> Create = delegate
{
    return new TestClass();
};

public void Test3()
{
    var obj = Create();
    GC.KeepAlive(obj);
}

TestClass x = new TestClass();
public void Test4()
{
    GC.KeepAlive(x);
}

public class TestClass
{
}
查看更多
登录 后发表回答