为了账号安全,请及时绑定邮箱和手机立即绑定

比较一下以“反射”和“表达式”执行方法的性能差异

标签:
架构

由于频繁地使用反射会影响性能,所以ASP.NET MVC采用了表达式树的方式来执行目标Action方法。具体来说,ASP.NET MVC会构建一个表达式来体现针对目标Action方法的执行,并且将该表达式编译成可执行代码。编译后的可执行代码体现为一个委托对象,该委托对象会被缓存起来以用于针对同一个Action方法的执行。为了让大家能够和直观地理解两种(直接利用反射和利用表达式编译后的委托对象)方法执行在性能上的差异,我们来做一个简单的实例演示。我们在一个控制台应用中定义了如下一个Foobar类型,它的Invoke方法就是我们需要测试的目标方法。简单起见,我们没有为它定义任何参数,方法本身也不需要执行任何具体操作。

   1: public class Foobar

   

   2: {

   

   3:     public void Invoke(){}

   

   4: }

具体的测试程序如下所示。三个静态属性Target、Method和Executor分别代表执行的目标对象、目标方法和表达式编译后生成的委托对象,后者通过调用静态方法CreateExecutor方法创建。

   1: class Program

   

   2: {

   

   3:

   

   4:     public static Foobar Target { get; private set; }

   

   5:     public static MethodInfo Method { get; private set; }

   

   6:     public static Action<Foobar> Executor { get; private set; }

   

   7:

   

   8:     private static object[] args = new object[0];

   

   9:

   

  10:     private static Action<Foobar> CreateExecutor(MethodInfo method)

   

  11:     {

   

  12:         ParameterExpression target = Expression.Parameter(typeof(Foobar),"target");

   

  13:         Expression expression = Expression.Call(target, method);

   

  14:         return Expression.Lambda<Action<Foobar>>(expression, target).Compile();

   

  15:     }

   

  16:

   

  17:     static Program()

   

  18:     {

   

  19:         Target = new Foobar();

   

  20:         Method = typeof(Foobar).GetMethod("Invoke");

   

  21:         Executor = CreateExecutor(Method);

   

  22:     }

   

  23:

   

  24:     static void Main()

   

  25:     {

   

  26:         Console.WriteLine("{0,-10}{1,-12}{2}", "Times", "Reflection", "Expression");

   

  27:         Test(100000);

   

  28:         Test(1000000);

   

  29:         Test(10000000);

   

  30:     }

   

  31:

   

  32:     private static void Test(int times)

   

  33:     {

   

  34:         Stopwatch stopwatch = new Stopwatch();

   

  35:

   

  36:         stopwatch.Start();

   

  37:         for (int i = 0; i < times; i++)

   

  38:         {

   

  39:             Method.Invoke(Target, args);

   

  40:         }

   

  41:         long elapsed1 = stopwatch.ElapsedMilliseconds;

   

  42:

   

  43:         stopwatch.Restart();

   

  44:         for (int i = 0; i < times; i++)

   

  45:         {

   

  46:             Executor(Target);

   

  47:         }

   

  48:         long elapsed2 = stopwatch.ElapsedMilliseconds;

   

  49:

   

  50:         Console.WriteLine("{0,-10}{1,-12}{2}", times, elapsed1, elapsed2);

   

  51:     }

   

  52: }

测试方法Test的参数times表示我们执行目标方法的次数。在该方法中,我们调用MethodInfo对象的Invoke方法以反射的形式执行目标方法,然后利用Executor属性表示的委托对象来执行目标方法,并将它们执行的时间(以毫秒为单位)输出来。在作为程序入口的Main方法中,我们先后三个调用Test方法,并将执行目标方法的次数分别设置为100,000(十万)、1,000,000(百万)和10,000,000(千万)。运行程序后我们会在控制台上得到如下所示的输出结果,可以看出直接采用反射方式执行某个方法确实在性能上要差一些,但是差异其实不算明显。很多人总是觉得在程序中使用反射会对性能造成很大的影响,其实在我看来在很多情况下反射本身都不是造成性能瓶颈的元凶。

   1: Times      Reflection     Expression

   

   2: 100000     34             2

   

   3: 1000000    273            28

   

   4: 10000000   2627           284
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消