反射(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#基礎知識梳理系列十五:反射

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM