替代反射调用的几种方式及性能测试

发表于:2013-7-10 11:00

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:鹤冲天    来源:51Testing软件测试网采编

分享:

  下面是几种方法的性能测试。

  性能对比测试

  这里对直接、反射发出、dynamic 、表达式树编译、反射五种调用方式进行性能对比测试。

  测试结果

  先给出测试的结果:

  从上图中可以看出:

  直接调用性能最佳;

  反射发出和表达式树两种方式性能相当,速度接近直接调用;

  dynamic 性能居中,也不错;

  反射方式性能最差。

  另外说明两点:

  本次测试仅针对 MyMath.Add 方法,其参数和返回值都是值类型,反射调用时存在大量装箱、拆箱。如果测试方法的参数和返回值都是引用类型,反射方式与其它方式间的差距会小些。

  从上图可以看出这几次方式性能差别较大,但此结果是重复 100 万次的情况下得出的。考虑单次调用,反射只比直接调用慢 381 纳秒。如果你的代码不是位于循环的中心或是系统的瓶颈,调用次数不多,性能差异可以完全忽略。

  测试代码

  以下是测试用代码,仅参考:

using System;
using System.Linq.Expressions;
using System.Reflection.Emit;
public class MyMath {
public int Add(int a, int b) { return a + b; }
}
class Program {
static void Main(string[] args) {
int result;
var math = new MyMath();
var count = 1000000;
Console.WriteLine("数据量:" + count);
Console.WriteLine("-----------------------------r\n");
using (Profiler.Step("循环:{0} ms")) {
for (int i = 0; i < count; i++)
result = 1;
}
using (Profiler.Step("直接调用 :{0} ms")) {
for (int i = 0; i < count; i++)
result = math.Add(i, i);
}
using (Profiler.Step("反射发出:{0} ms")) {
var emitAdd = BuildEmitAddFunc();
for (int i = 0; i < count; i++)
result = emitAdd(math, i, i);
}
using (Profiler.Step("表达式树:{0} ms")) {
var expressionAdd = BuildExpressionAddFunc();
for (int i = 0; i < count; i++)
result = expressionAdd(math, i, i);
}
using (Profiler.Step("dynamic 调用:{0} ms")) {
dynamic d = math;
for (int i = 0; i < count; i++)
result = d.Add(i, i);
}
using (Profiler.Step("反射调用:{0} ms")) {
var add = typeof(MyMath).GetMethod("Add");
for (int i = 0; i < count; i++)
result = (int)add.Invoke(math, new object[] { i, i });
}
Console.WriteLine("\r\n\r\n测试完成,任意键退出...");
Console.ReadKey();
}
static Func<MyMath, int, int, int> BuildExpressionAddFunc() {
var add = typeof(MyMath).GetMethod("Add");
var math = Expression.Parameter(typeof(MyMath));
var a = Expression.Parameter(typeof(int), "a");
var b = Expression.Parameter(typeof(int), "b");
var body = Expression.Call(math, add, a, b);
var lambda = Expression.Lambda<Func<MyMath, int, int, int>>(body, math, a, b);
return lambda.Compile();
}
static Func<MyMath, int, int, int> BuildEmitAddFunc() {
var add = typeof(MyMath).GetMethod("Add");
var dynamicMethod = new DynamicMethod("", typeof(int), new[] { typeof(MyMath), typeof(int), typeof(int) });
var il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Callvirt, add);
il.Emit(OpCodes.Ret);
return (Func<MyMath, int, int, int>)dynamicMethod.CreateDelegate(typeof(Func<MyMath, int, int, int>));
}
}

  Profiler 是我写的一个类,用于简化测试:

using System;
using System.Diagnostics;
public class Profiler : IDisposable {
private Stopwatch watch;
private string message;
private Profiler(string message) {
this.watch = new Stopwatch();
this.watch.Start();
this.message = message;
}
public void Dispose() {
watch.Stop();
Console.WriteLine(message, watch.ElapsedMilliseconds);
Console.WriteLine();
}
public static IDisposable Step(string message) {
return new Profiler(message);
}
public static T Inline<T>(string message, Func<T> func) {
using (new Profiler(message))
return func();
}
}

  总结

  综上所述,我们可以使用 .NET 2.0 的 DynamicMethod,.NET 3.5 引入的 Expression Tree、.NET 4 新增的 dynamic 来替换反射调用,带来更好的性能。

  希望本文对大家有所帮助,也希望整天忙于项目、疲于工作的朋友抽点时间学习下 .NET Framework 的一些“新”特性。

22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号