Pac*_*ier 30 .net performance instantiation activator
System.Activator.CreateInstance(T)方法是否有性能问题(因为我怀疑它使用反射)大到足以阻止我们随便使用它?
ang*_*son 49
与往常一样,回答关于性能的问题的唯一正确方法是实际测量代码.
这是一个示例LINQPad程序,用于测试:
和往常一样,在性能程序中加入一些盐,这里可能存在错误导致结果偏差的错误.
输出(时间值以毫秒为单位):
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
{
}
Run Code Online (Sandbox Code Playgroud)
这是一个示例C#.NET 4.0程序,用于测试:
输出(时间值以毫秒为单位,来自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
Run Code Online (Sandbox Code Playgroud)
这是从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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这取决于您的使用案例.如果您需要非常高的性能并且正在创建许多对象,那么使用Activator.CreateInstance可能是个问题.
但在大多数情况下,它足够快,它是一种非常强大的创建对象的方法.
实际上,大多数IoC容器/服务定位器/无论您调用它们,都使用此方法来创建您请求的类型的对象.
如果您担心性能不够好,那么您应该对应用程序进行概要分析,并测量是否存在瓶颈及其位置.我的猜测是打电话Activator.CreateInstance不是你的问题.
是的,呼叫之间存在性能差异
(MyClass)Activator.CreateInstance(typeof(MyClass));
Run Code Online (Sandbox Code Playgroud)
和
new MyClass();
Run Code Online (Sandbox Code Playgroud)
后者更快的地方.但是,确定速度下降是否足够大取决于您的域名.在90%的情况下,这不是问题.另请注意,对于值类型,Activator.CreateInstance由于涉及拆箱而再次变慢.
但这里有一个问题:对于泛型类型,它们是相似的.new T()内部调用Activator.CreateInstance<T>(),然后调用RuntimeType.CreateInstanceDefaultCtor(...).因此,如果你有一个通用的方法来创建新的实例T,那么它应该无关紧要,尽管有一个new()约束和调用new T()更具可读性.这是Jon Skeet关于这个主题的相关链接.
| 归档时间: |
|
| 查看次数: |
12203 次 |
| 最近记录: |