System.Activator.CreateInstance(T)是否存在足以阻止我们随意使用它的性能问题?

时间:2011-05-20 09:01:53

标签: .net performance instantiation activator

System.Activator.CreateInstance(T)方法是否存在性能问题(因为我怀疑它使用反射)大到足以阻止我们随便使用它?

5 个答案:

答案 0 :(得分:49)

与往常一样,回答关于性能的问题的唯一正确方法是实际测量代码。

以下是测试的示例LINQPad程序:

  • Activator.CreateInstance
  • new T()
  • 调用调用new T()
  • 的委托

与往常一样,使用性能程序时,可能存在一些错误导致结果出现偏差。

输出(时间值以毫秒为单位):

Test1 - Activator.CreateInstance<T>() 
12342 

Test2 - new T() 
1119 

Test3 - Delegate 
1530 

Baseline 
578 

请注意,上述时间适用于对象的100.000.000(1亿)个构造。开销可能不是您的程序的真正问题。

警告性结论是Activator.CreateInstance<T>花费大约11倍的时间来完成与new T()相同的工作,而代表大约需要1.5倍的时间。请注意,此处的构造函数不执行任何操作,因此我只尝试测量不同方法的开销。

编辑:我添加了一个不构造对象的基线调用,但是完成了其余的事情,并将其计时。以此为基准,看起来委托比简单的new()花费的时间多75%,而Activator.CreateInstance则需要大约1100%的时间。

然而,这是微优化。如果你真的需要这样做,并且找出一些时间关键代码的最后一次性能,我会手动编写一个委托代替使用,或者如果那是不可能的,即。你需要在运行时提供类型,我会使用Reflection.Emit动态生成该委托。

无论如何,这是我的真实答案:

  

如果您遇到性能问题,请先测量一下您的瓶颈所在。是的,上面的时间可能表明Activator.CreateInstance比动态构建的委托有更多的开销,但是在你获得(甚至不得不)达到这个优化级别之前,你的代码库可能会有更大的鱼。强>

只是为了确保我真的回答你的具体问题:不,我不会阻止使用Activator.CreateInstance。您应该知道它使用反射,以便您知道如果这会超出您的瓶颈分析列表,那么您可以对它做一些事情,但它使用反射的事实并不意味着它瓶颈。

该计划:

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
{
}

答案 1 :(得分:9)

以下是测试的示例C#.NET 4.0程序:

  • Activator.CreateInstance
  • new T()
  • 调用调用new T()
  • 的委托
  • generic new()
  • 使用通用
  • 的Activator.CreateInstance
  • 使用通用和非默认绑定的Activator.CreateInstance(例如调用内部构造函数)

输出(时间值以毫秒为单位,来自2014年x86发布版本的强劲机器):

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

这是从Lasse V. Karlsen的回答中采用的,但重要的是包括泛型。请注意,指定绑定会使使用泛型减慢Activator的速度超过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);
        }
    }
}

答案 2 :(得分:7)

这取决于您的使用案例。如果您需要非常高的性能并且正在创建许多对象,那么使用Activator.CreateInstance可能会有问题。

但在大多数情况下它会足够快,而且它是一种非常强大的创建对象的方法。

事实上,大多数IoC容器/服务定位器/您调用它们的任何方法都使用此方法来创建您请求的类型的对象。

如果您担心性能不够好,那么您应该对应用程序进行概要分析,并测量是否存在瓶颈及其位置。我的猜测是,Activator.CreateInstance的来电不会是您的问题。

答案 3 :(得分:5)

是的,调用

之间存在性能差异
(MyClass)Activator.CreateInstance(typeof(MyClass));

new MyClass();

后者更快。但是,确定速度下降是否足够大取决于您的域名。在90%的情况下,这不是问题。另请注意,对于值类型,由于涉及unboxingActivator.CreateInstance再次变慢。

但是这里是catch :对于泛型类型,它们是相似的。 new T()在内部拨打Activator.CreateInstance<T>(),然后拨打RuntimeType.CreateInstanceDefaultCtor(...)。因此,如果您使用通用方法创建T的新实例,那么它应该无关紧要,尽管具有new()约束并且调用new T()更具可读性。这是Jon Skeet关于这个主题的relevant link

答案 4 :(得分:0)

是的,实际上它存在性能问题(与new()相比),因为它使用Reflection,并且静态编译器特别在将参数传递给它(将参数发送给类的构造函数)而不是使用默认值时进行检查构造函数(如下所示)

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

我认为是否使用它取决于两件事:

首先,您的应用程序类型是可扩展的(当然是典型的流量)

第二个(更重要的是)如何使用Activator.CreateInstance方法和位置,例如,如果您在每个请求中使用一个或多个构造函数参数(如我提到的使用构造函数参数,速度慢了近十分之一)与不使用无参数(默认构造函数)相比,您的应用程序的性能几乎会大幅下降,但对于另一个实例,如果您使用一次(例如,在application_start上)并且没有构造函数参数< / strong>几乎像new关键字

这是new()Activator.CreateInstanceType.GetInstance()之间的详细基准比较

new vs Func vs Activator.CreateInstance()