调用 Delegate.DynamicInvoke 与 Func<object>()
Calling Delegate.DynamicInvoke vs Func<object>()
我一直在对一些创建类型实例的代码进行基准测试,这个结果对我来说似乎很奇怪:
Delegate deleg = Expression.Lambda(Expression.New(_type)).Compile();
// deleg.DynamicInvoke();
对
Func<object> func = Expression.Lambda<Func<object>>(Expression.New(_type)).Compile();
// func();
使用 BenchmarDotNet 给出(平均值,核心):
- 代表:501.790 纳秒
- 函数:4.710 纳秒
谁知道为什么差别这么大?
完整的基准测试:
[ClrJob(baseline: true), CoreJob, CoreRtJob]
[RPlotExporter, RankColumn]
public class Benchmarks
{
private Type _type;
private ConstructorInfo _constructor;
private Delegate _delegate;
private Func<object> _func;
[GlobalSetup]
public void GlobalSetup()
{
_type = typeof(TestClass);
_constructor = _type.GetConstructor(Type.EmptyTypes);
_delegate = Expression.Lambda(Expression.New(_type)).Compile();
_func = Expression.Lambda<Func<object>>(Expression.New(_type)).Compile();
}
[Benchmark(Baseline = true)]
public object Instanciate_Using_New()
{
return new TestClass();
}
[Benchmark]
public object Instanciate_Using_Activator()
{
return Activator.CreateInstance(_type);
}
[Benchmark]
public object Instanciate_Using_Constructor()
{
return _constructor.Invoke(null);
}
[Benchmark]
public object Instanciate_Using_Expression_Delegate()
{
return _delegate.DynamicInvoke();
}
[Benchmark]
public object Instanciate_Using_Expression_Func()
{
return _func();
}
}
性能差异是由Invoke()
(快)和DynamicInvoke()
(慢)的性能不同造成的。查看直接调用 Func<object>
类型委托生成的 IL 时,您会发现生成的 IL 实际上会调用 Invoke()
方法:
static void TestInvoke(Func<object> func) {
func();
}
以上代码编译成如下所示的 IL 代码(在调试版本中):
.method private hidebysig static void TestInvoke(class [mscorlib]System.Func`1<object> func) cil managed {
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0 // func
IL_0002: callvirt instance !0/*object*/ class [mscorlib]System.Func`1<object>::Invoke()
IL_0007: pop
IL_0008: ret
} // end of method Program::TestInvoke
并且 Invoke()
方法比 DynamicInvoke()
方法快得多,因为它基本上不需要解析委托的类型(因为它已经知道)。以下对另一个问题的回答更详细地解释了 Invoke()
和 DynamicInvoke()
的区别:
以下非常简化且可能不是很准确的测试显示了性能上的巨大差异。如您所见,我什至使用了相同的委托,只是以不同的方式调用它:
class Program {
static void Main(string[] args) {
var ex = Expression.Lambda<Func<object>>(Expression.New(typeof(object))).Compile();
Stopwatch timer = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++) TestInvoke(ex);
Console.WriteLine($"Invoke():\t\t{timer.Elapsed.ToString()}");
timer = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++) TestDynamicInvoke(ex);
Console.WriteLine($"DynamicInvoke():\t{timer.Elapsed.ToString()}");
Console.ReadKey(true);
}
static void TestInvoke(Func<object> func) {
func();
}
static void TestDynamicInvoke(Delegate deleg) {
deleg.DynamicInvoke();
}
}
在我家里的 PC 上使用发布版本的结果,没有附加调试器(如上所述,我知道这个简单的测试可能不是很准确,但它证明了性能上的巨大差异)
Invoke(): 00:00:00.0080935
DynamicInvoke(): 00:00:00.8382236
我一直在对一些创建类型实例的代码进行基准测试,这个结果对我来说似乎很奇怪:
Delegate deleg = Expression.Lambda(Expression.New(_type)).Compile();
// deleg.DynamicInvoke();
对
Func<object> func = Expression.Lambda<Func<object>>(Expression.New(_type)).Compile();
// func();
使用 BenchmarDotNet 给出(平均值,核心):
- 代表:501.790 纳秒
- 函数:4.710 纳秒
谁知道为什么差别这么大?
完整的基准测试:
[ClrJob(baseline: true), CoreJob, CoreRtJob]
[RPlotExporter, RankColumn]
public class Benchmarks
{
private Type _type;
private ConstructorInfo _constructor;
private Delegate _delegate;
private Func<object> _func;
[GlobalSetup]
public void GlobalSetup()
{
_type = typeof(TestClass);
_constructor = _type.GetConstructor(Type.EmptyTypes);
_delegate = Expression.Lambda(Expression.New(_type)).Compile();
_func = Expression.Lambda<Func<object>>(Expression.New(_type)).Compile();
}
[Benchmark(Baseline = true)]
public object Instanciate_Using_New()
{
return new TestClass();
}
[Benchmark]
public object Instanciate_Using_Activator()
{
return Activator.CreateInstance(_type);
}
[Benchmark]
public object Instanciate_Using_Constructor()
{
return _constructor.Invoke(null);
}
[Benchmark]
public object Instanciate_Using_Expression_Delegate()
{
return _delegate.DynamicInvoke();
}
[Benchmark]
public object Instanciate_Using_Expression_Func()
{
return _func();
}
}
性能差异是由Invoke()
(快)和DynamicInvoke()
(慢)的性能不同造成的。查看直接调用 Func<object>
类型委托生成的 IL 时,您会发现生成的 IL 实际上会调用 Invoke()
方法:
static void TestInvoke(Func<object> func) {
func();
}
以上代码编译成如下所示的 IL 代码(在调试版本中):
.method private hidebysig static void TestInvoke(class [mscorlib]System.Func`1<object> func) cil managed {
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0 // func
IL_0002: callvirt instance !0/*object*/ class [mscorlib]System.Func`1<object>::Invoke()
IL_0007: pop
IL_0008: ret
} // end of method Program::TestInvoke
并且 Invoke()
方法比 DynamicInvoke()
方法快得多,因为它基本上不需要解析委托的类型(因为它已经知道)。以下对另一个问题的回答更详细地解释了 Invoke()
和 DynamicInvoke()
的区别:
以下非常简化且可能不是很准确的测试显示了性能上的巨大差异。如您所见,我什至使用了相同的委托,只是以不同的方式调用它:
class Program {
static void Main(string[] args) {
var ex = Expression.Lambda<Func<object>>(Expression.New(typeof(object))).Compile();
Stopwatch timer = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++) TestInvoke(ex);
Console.WriteLine($"Invoke():\t\t{timer.Elapsed.ToString()}");
timer = Stopwatch.StartNew();
for (int i = 0; i < 1000000; i++) TestDynamicInvoke(ex);
Console.WriteLine($"DynamicInvoke():\t{timer.Elapsed.ToString()}");
Console.ReadKey(true);
}
static void TestInvoke(Func<object> func) {
func();
}
static void TestDynamicInvoke(Delegate deleg) {
deleg.DynamicInvoke();
}
}
在我家里的 PC 上使用发布版本的结果,没有附加调试器(如上所述,我知道这个简单的测试可能不是很准确,但它证明了性能上的巨大差异)
Invoke(): 00:00:00.0080935
DynamicInvoke(): 00:00:00.8382236