很多人都說使用反射會有性能問題,那到底會比直接調用慢多少呢,下麵就來測試一下。 直接調用vs反射調用 下麵就來寫個demo來驗證下直接調用和反射調用的性能差異,代碼如下: 測試結果: 從100萬次調用結果來看,確實就像很多人所說的,兩者在性能上具有數量級的差距。 為什麼反射有性能損失 既然反射性能有 ...
很多人都說使用反射會有性能問題,那到底會比直接調用慢多少呢,下麵就來測試一下。
直接調用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的方法來提高反射的性能,但是相對來說,上面這個方法是最簡單的。
至此,整個反射的總結就完成了!