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

由于频繁地使用反射会影响性能,所以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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

Go Channel 应用模式(二)

eapache/channels提供了一些channel应用模式的方法,比如上面的扇入扇出模式等。

16530
来自专栏java技术学习之道

Java设计模式——代理模式实现及原理

13930
来自专栏ml

错误笔记-----今天在敲代码的时候,出现了一个问题!

 if(x!=Father(x))         //这条代码-----其实我定义的是个数组,然而我却用错了括号【】我用了(), //D:\Microsoft...

357130
来自专栏Golang语言社区

Go Channel 应用模式(二)

eapache/channels提供了一些channel应用模式的方法,比如上面的扇入扇出模式等。

11830
来自专栏偏前端工程师的驿站

意译:《JVM Internals》

译者语                                  为加深对JVM的了解和日后查阅时更方便,于是对原文进行翻译。内容是建立在我对JVM的认...

25270
来自专栏大内老A

Dora.Interception, 为.NET Core度身打造的AOP框架:不一样的Interceptor定义方式

相较于社区其他主流的AOP框架,Dora.Interception在Interceptor提供了完全不同的编程方式。我们并没有为Interceptor定义一个接...

11650
来自专栏程序员互动联盟

【C语言基础】结构体赋值

结构体在 C 程序中使用的较为频繁,能对数据有一定的封装的作用。对一个结构体赋值时,经常采用的方式是,分别对其成员变量赋值。那么能否将一个结构体用赋值号(“=”...

47670
来自专栏项勇

Python : ex1

9430
来自专栏流柯技术学院

TestNG 三 测试方法

测试方法是可以带有参数的。每个测试方法都可以带有任意数量的参数,并且可以通过使用TestNG的@Parameters向方法传递正确的参数。

11730
来自专栏老付的网络博客

详解.net中IL语言

中间语言,又称(IL语言)。充当Clr与.net 平台的中间语言,比如用C#编写程序,编译器首先是把C#代码转译成IL语言,最终由Clr解释执行,下面我们学习下...

18220

扫码关注云+社区

领取腾讯云代金券