反射调用性能比较(附源码)
2012-06-29 17:33 田志良 阅读(6797) 评论(22) 编辑 收藏 举报下面通过编写Demo的方式,验证各种反射的性能。
1、传统方式反射
1 Type t = typeof(Person); 2 MethodInfo methodInfo = t.GetMethod("Say"); 3 Person person = new Person(); 4 string word = "hello"; 5 Person p = null; 6 object[] param = new object[] { word, p, 3 }; 7 int TestTimes = 1000000; //测试次数,可自行调节看效果 8 9 #region 传统方式反射 10 try 11 { 12 Stopwatch watch = new Stopwatch(); 13 watch.Start(); 14 for (int i = 0; i < TestTimes; i++) 15 { 16 methodInfo.Invoke(person, param); 17 } 18 watch.Stop(); 19 Console.WriteLine(TestTimes.ToString() + " times invoked by Reflection: " + watch.ElapsedMilliseconds + "ms"); 20 } 21 catch (System.Exception ex) 22 { 23 Console.WriteLine("传统方式反射 直接错误:" + ex.Message); 24 Console.WriteLine("传统方式反射 内部错误:" + ex.InnerException.Message); 25 } 26 #endregion
2、实例化反射
1 #region 实例化反射 2 try 3 { 4 //Stopwatch watch = new Stopwatch(); 5 //watch.Start(); 6 //for (int i = 0; i < TestTimes; i++) 7 //{ 8 // Assembly Asm = Assembly.Load("testInvoke");//反射出空间 9 // Type type = Asm.GetType("testInvoke.Person");//反射出空间下的类 10 // object AssClas = Activator.CreateInstance(type);//动态实例化反射回来的指定空间下的指定类 11 // IPerson objPerson = (Person)AssClas; ////转换为接口类型 12 // objPerson.Say(ref word, out p, 3); 13 //} 14 //watch.Stop(); 15 //Console.WriteLine(TestTimes.ToString() + " times invoked by InstanceReflection@1: " + watch.ElapsedMilliseconds + "ms"); 16 17 Assembly Asm = Assembly.Load("testInvoke");//反射出空间 18 Type type = Asm.GetType("testInvoke.Person");//反射出空间下的类 19 object AssClas = Activator.CreateInstance(type);//动态实例化反射回来的指定空间下的指定类 20 IPerson objPerson = (Person)AssClas; //转换为接口类型 21 Stopwatch watch = new Stopwatch(); 22 watch.Start(); 23 for (int i = 0; i < TestTimes; i++) 24 { 25 objPerson.Say(ref word, out p, 3); 26 } 27 watch.Stop(); 28 Console.WriteLine(TestTimes.ToString() + " times invoked by InstanceReflection@2: " + watch.ElapsedMilliseconds + "ms"); 29 30 } 31 catch (System.Exception ex) 32 { 33 Console.WriteLine("实例化反射 错误:" + ex.Message); 34 } 35 #endregion
3、快速反射
1 #region 快速反射 2 try 3 { 4 Stopwatch watch1 = new Stopwatch(); 5 FastInvoke.FastInvokeHandler fastInvoker = FastInvoke.GetMethodInvoker(methodInfo); 6 watch1.Start(); 7 for (int i = 0; i < TestTimes; i++) 8 { 9 fastInvoker(person, param); 10 } 11 watch1.Stop(); 12 Console.WriteLine(TestTimes.ToString() + " times invoked by FastInvoke: " + watch1.ElapsedMilliseconds + "ms"); 13 } 14 catch (System.Exception ex) 15 { 16 Console.WriteLine("快速反射 错误:" + ex.Message); 17 } 18 #endregion
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Reflection.Emit; 6 using System.Reflection; 7 8 namespace testInvoke 9 { 10 class FastInvoke 11 { 12 public delegate object FastInvokeHandler(object target, object[] paramters); 13 14 static object InvokeMethod(FastInvokeHandler invoke, object target, params object[] paramters) 15 { 16 return invoke(null, paramters); 17 } 18 19 public static FastInvokeHandler GetMethodInvoker(MethodInfo methodInfo) 20 { 21 DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module); 22 ILGenerator il = dynamicMethod.GetILGenerator(); 23 ParameterInfo[] ps = methodInfo.GetParameters(); 24 Type[] paramTypes = new Type[ps.Length]; 25 for (int i = 0; i < paramTypes.Length; i++) 26 { 27 if (ps[i].ParameterType.IsByRef) 28 paramTypes[i] = ps[i].ParameterType.GetElementType(); 29 else 30 paramTypes[i] = ps[i].ParameterType; 31 } 32 LocalBuilder[] locals = new LocalBuilder[paramTypes.Length]; 33 34 for (int i = 0; i < paramTypes.Length; i++) 35 { 36 locals[i] = il.DeclareLocal(paramTypes[i], true); 37 } 38 for (int i = 0; i < paramTypes.Length; i++) 39 { 40 il.Emit(OpCodes.Ldarg_1); 41 EmitFastInt(il, i); 42 il.Emit(OpCodes.Ldelem_Ref); 43 EmitCastToReference(il, paramTypes[i]); 44 il.Emit(OpCodes.Stloc, locals[i]); 45 } 46 if (!methodInfo.IsStatic) 47 { 48 il.Emit(OpCodes.Ldarg_0); 49 } 50 for (int i = 0; i < paramTypes.Length; i++) 51 { 52 if (ps[i].ParameterType.IsByRef) 53 il.Emit(OpCodes.Ldloca_S, locals[i]); 54 else 55 il.Emit(OpCodes.Ldloc, locals[i]); 56 } 57 if (methodInfo.IsStatic) 58 il.EmitCall(OpCodes.Call, methodInfo, null); 59 else 60 il.EmitCall(OpCodes.Callvirt, methodInfo, null); 61 if (methodInfo.ReturnType == typeof(void)) 62 il.Emit(OpCodes.Ldnull); 63 else 64 EmitBoxIfNeeded(il, methodInfo.ReturnType); 65 66 for (int i = 0; i < paramTypes.Length; i++) 67 { 68 if (ps[i].ParameterType.IsByRef) 69 { 70 il.Emit(OpCodes.Ldarg_1); 71 EmitFastInt(il, i); 72 il.Emit(OpCodes.Ldloc, locals[i]); 73 if (locals[i].LocalType.IsValueType) 74 il.Emit(OpCodes.Box, locals[i].LocalType); 75 il.Emit(OpCodes.Stelem_Ref); 76 } 77 } 78 79 il.Emit(OpCodes.Ret); 80 FastInvokeHandler invoder = (FastInvokeHandler)dynamicMethod.CreateDelegate(typeof(FastInvokeHandler)); 81 return invoder; 82 } 83 84 private static void EmitCastToReference(ILGenerator il, System.Type type) 85 { 86 if (type.IsValueType) 87 { 88 il.Emit(OpCodes.Unbox_Any, type); 89 } 90 else 91 { 92 il.Emit(OpCodes.Castclass, type); 93 } 94 } 95 96 private static void EmitBoxIfNeeded(ILGenerator il, System.Type type) 97 { 98 if (type.IsValueType) 99 { 100 il.Emit(OpCodes.Box, type); 101 } 102 } 103 104 private static void EmitFastInt(ILGenerator il, int value) 105 { 106 switch (value) 107 { 108 case -1: 109 il.Emit(OpCodes.Ldc_I4_M1); 110 return; 111 case 0: 112 il.Emit(OpCodes.Ldc_I4_0); 113 return; 114 case 1: 115 il.Emit(OpCodes.Ldc_I4_1); 116 return; 117 case 2: 118 il.Emit(OpCodes.Ldc_I4_2); 119 return; 120 case 3: 121 il.Emit(OpCodes.Ldc_I4_3); 122 return; 123 case 4: 124 il.Emit(OpCodes.Ldc_I4_4); 125 return; 126 case 5: 127 il.Emit(OpCodes.Ldc_I4_5); 128 return; 129 case 6: 130 il.Emit(OpCodes.Ldc_I4_6); 131 return; 132 case 7: 133 il.Emit(OpCodes.Ldc_I4_7); 134 return; 135 case 8: 136 il.Emit(OpCodes.Ldc_I4_8); 137 return; 138 } 139 140 if (value > -129 && value < 128) 141 { 142 il.Emit(OpCodes.Ldc_I4_S, (SByte)value); 143 } 144 else 145 { 146 il.Emit(OpCodes.Ldc_I4, value); 147 } 148 } 149 } 150 }
4、不用反射,直接调用
1 #region 直接调用 2 try 3 { 4 Stopwatch watch2 = new Stopwatch(); 5 watch2.Start(); 6 for (int i = 0; i < TestTimes; i++) 7 { 8 person.Say(ref word, out p, 3); 9 } 10 watch2.Stop(); 11 Console.WriteLine(TestTimes.ToString() + " times invoked by DirectCall: " + watch2.ElapsedMilliseconds + "ms"); 12 } 13 catch (System.Exception ex) 14 { 15 Console.WriteLine("直接调用 错误:" + ex.Message); 16 } 17 #endregion
以上4种调用方式,100万次调用结果如下:
所以得出以下结论:
1. 不用反射,直接调用,效率最高。
2. 实例化反射,效率次之。
3. 快速反射,效率次之。
4. 传统反射,效率最差。
以上调用方式,后3种调用方式虽然效率有先后,但性能在一个数量级上,与传统反射相比,优越性较明显。
另外补充一点,实例化反射如果算上创建实例的性能损耗,试验结果如下图:
所以,如果需要频繁创建实例,建议不要采用实例化反射。
源码下载:files.cnblogs.com/tianzhiliang/testInvoke.rar
作者:田志良