zoukankan      html  css  js  c++  java
  • 反射(4)反射性能问题:直接调用vs反射调用

      很多人都说使用反射会有性能问题,那到底会比直接调用慢多少呢,下面就来测试一下。

    直接调用vs反射调用

    下面就来写个demo来验证下直接调用和反射调用的性能差异,代码如下:

     1 namespace ConsoleApplication7
     2 {
     3     class Program
     4     {
     5         static void Main(string[] args)
     6         {
     7             //比较直接调用和反射调用的性能差异
     8             //7ms vs 365ms
     9             int times = 1000000;
    10             var program = new Program();
    11             CodeTimerHelper.Initialize();
    12 
    13             CodeTimerHelper.Time("直接调用", times, () =>
    14             {
    15                 program.Call();
    16             });
    17 
    18             var t = typeof(Program);
    19             var obj = Activator.CreateInstance(t);
    20             CodeTimerHelper.Time("反射调用", times, () =>
    21             {
    22                 t.InvokeMember("Call", BindingFlags.InvokeMethod, null, obj, null);
    23             });
    24 
    25             Console.ReadKey();
    26         }
    27 
    28         /// <summary>
    29         /// 测试方法
    30         /// </summary>
    31         public void Call()
    32         {
    33         }
    34 
    35     }
    36 }

    测试结果:

    从100万次调用结果来看,确实就像很多人所说的,两者在性能上具有数量级的差距。 

    为什么反射有性能损失

    既然反射性能有损失,那具体损失在哪里呢?

    1,反射是基于程序集和元数据的,在使用反射的时候,会搜索元数据,而元数据是基于字符串的,并且无法预编译,所以这一系列的操作对性能有影响。

    2,大量的装箱拆箱也对性能有影响。由于我们对目标类型是未知的,而且向方法传递的参数通常是object类型的,所以会有大量的装箱和拆箱。

    反射性能优化方案

    我们已经知道使用反射有性能问题,但是有些场景下又不得不使用反射技术,所以要想办法优化反射性能。

    这里就引用老赵公开的System.Linq.Expressions.Expression<TDelegate>表达式树的类,与直接调用进行对比,代码如下:

    1 //3,基于表达式树
    2 var methodInfo = t.GetMethod("Call");
    3 var executor = new DynamicMethodExecutor(methodInfo);
    4 CodeTimerHelper.Time("Dynamic executor", times, () =>
    5 {
    6     executor.Execute(obj, null);
    7 });

    测试结果:

    哇,同样的100万次调用,使用DynamicMethodExecutor调用跟直接调用的性能相差无几。

    附DynamicMethodExecutor的封装代码:

     1 /// <summary>
     2 /// 
     3 /// </summary>
     4 public class DynamicMethodExecutor
     5 {
     6     private Func<object, object[], object> m_execute;
     7 
     8     public DynamicMethodExecutor(MethodInfo methodInfo)
     9     {
    10         this.m_execute = this.GetExecuteDelegate(methodInfo);
    11     }
    12 
    13     public object Execute(object instance, object[] parameters)
    14     {
    15         return this.m_execute(instance, parameters);
    16     }
    17 
    18     private Func<object, object[], object> GetExecuteDelegate(MethodInfo methodInfo)
    19     {
    20         // parameters to execute
    21         ParameterExpression instanceParameter = Expression.Parameter(typeof(object), "instance");
    22         ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");
    23 
    24         // build parameter list
    25         List<Expression> parameterExpressions = new List<Expression>();
    26         ParameterInfo[] paramInfos = methodInfo.GetParameters();
    27         for (int i = 0; i < paramInfos.Length; i++)
    28         {
    29             // (Ti)parameters[i]
    30             BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
    31             UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);
    32             parameterExpressions.Add(valueCast);
    33         }
    34 
    35         // non-instance for static method, or ((TInstance)instance)
    36         Expression instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instanceParameter, methodInfo.ReflectedType);
    37 
    38         // static invoke or ((TInstance)instance).Method
    39         MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, parameterExpressions);
    40 
    41         // ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...)
    42         if (methodCall.Type == typeof(void))
    43         {
    44             Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(methodCall, instanceParameter, parametersParameter);
    45             Action<object, object[]> execute = lambda.Compile();
    46             return (instance, parameters) =>
    47             {
    48                 execute(instance, parameters);
    49                 return null;
    50             };
    51         }
    52         else
    53         {
    54             UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));
    55             Expression<Func<object, object[], object>> lambda = Expression.Lambda<Func<object, object[], object>>(castMethodCall, instanceParameter, parametersParameter);
    56             return lambda.Compile();
    57         }
    58     }

     除了使用linq的表达式树生成Delegate的方法外,还有比如,CodeDom生成代码并动态编译,或者使用Emit来直接编写IL的方法来提高反射的性能,但是相对来说,上面这个方法是最简单的。

    至此,整个反射的总结就完成了!

    参考文章

    方法的直接调用,反射调用与……Lambda表达式调用

    C#基础知识梳理系列十五:反射

    二、什么是反射、反射可以做些什么

  • 相关阅读:
    Service Location Protocol SLP
    [http 1.1] M-POST
    安装 wbemcli
    [http 1.1] M-POST w3
    [CODEVS 1288]埃及分数
    [NOIp 2013]货车运输
    [测试题]gentree
    [USACO 07NOV]Cow Relays
    [USACO 13DEC]Vacation Planning(gold)
    [CODEVS 2495]水叮当的舞步
  • 原文地址:https://www.cnblogs.com/mcgrady/p/7001545.html
Copyright © 2011-2022 走看看