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

发布于 2024-11-08 23:27:02 字数 78 浏览 0 评论 0 原文

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

情域 2024-11-15 23:27:02

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

下面是一个测试 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 完成相同工作所需的时间大约是 new T() 的 11 倍,而委托则需要大约 11 倍的时间。大约是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
{
}

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
{
}
_失温 2024-11-15 23:27:02

下面是一个用于测试的 C# .NET 4.0 程序示例:

  • Activator.CreateInstance
  • new T()
  • 调用调用 new T() 的委托
  • 泛型 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);
        }
    }
}

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);
        }
    }
}
勿挽旧人 2024-11-15 23:27:02

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

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

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

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

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.

浪推晚风 2024-11-15 23:27:02

是的,调用之间存在性能差异

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

new MyClass();

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

但这里有一个问题:对于泛型类型,它们是相似的。 new T() 内部调用 Activator.CreateInstance(),后者又调用 RuntimeType.CreateInstanceDefaultCtor(...)。因此,如果您有一个通用方法来创建 T 的新实例,那么尽管有 new() 约束并调用 new ,但这并不重要T() 更具可读性。这是相关链接 关于乔恩·斯基特的主题。

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.

雨后彩虹 2024-11-15 23:27:02

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

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

在我看来,使用或不使用它取决于两件事:

首先是您的应用程序类型,当然还有它的规模(这是典型的流量)

,其次(更重要的是)您使用的方式和地点Activator.CreateInstance 方法,例如,如果您在每个带有一个或多个构造函数参数的请求上使用它(正如我提到的,使用构造函数参数几乎比使用不带参数(默认构造函数)慢十分之一),您的应用程序的性能几乎大幅下降,但在另一个例子中,如果您使用一次(例如在 application_start 上)并且没有构造函数参数,它几乎就像 这里的new 关键字

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

new 与 Func 与 Activator.CreateInstance()

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()

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文