代码改变世界

反射调用性能比较(附源码)

2012-06-29 17:33  田志良  阅读(...)  评论(...编辑  收藏

下面通过编写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

 

作者:田志良

出处:http://www.cnblogs.com/tianzhiliang/