文章出處

由于頻繁地使用反射會影響性能,所以ASP.NET MVC采用了表達式樹的方式來執行目標Action方法。具體來說,ASP.NET MVC會構建一個表達式來體現針對目標Action方法的執行,并且將該表達式編譯成可執行代碼。編譯后的可執行代碼體現為一個委托對象,該委托對象會被緩存起來以用于針對同一個Action方法的執行。為了讓大家能夠和直觀地理解兩種(直接利用反射和利用表達式編譯后的委托對象)方法執行在性能上的差異,我們來做一個簡單的實例演示。我們在一個控制臺應用中定義了如下一個Foobar類型,它的Invoke方法就是我們需要測試的目標方法。簡單起見,我們沒有為它定義任何參數,方法本身也不需要執行任何具體操作。

   1: public class Foobar
   2: {
   3:     public void Invoke(){}
   4: }

具體的測試程序如下所示。三個靜態屬性Target、Method和Executor分別代表執行的目標對象、目標方法和表達式編譯后生成的委托對象,后者通過調用靜態方法CreateExecutor方法創建。

   1: class Program
   2: {
   3:  
   4:     public static Foobar Target { get; private set; }
   5:     public static MethodInfo Method { get; private set; }
   6:     public static Action<Foobar> Executor { get; private set; }
   7:  
   8:     private static object[] args = new object[0];
   9:  
  10:     private static Action<Foobar> CreateExecutor(MethodInfo method)
  11:     { 
  12:         ParameterExpression target = Expression.Parameter(typeof(Foobar),"target");
  13:         Expression expression = Expression.Call(target, method);
  14:         return Expression.Lambda<Action<Foobar>>(expression, target).Compile();
  15:     }
  16:  
  17:     static Program()
  18:     {
  19:         Target = new Foobar();
  20:         Method = typeof(Foobar).GetMethod("Invoke");
  21:         Executor = CreateExecutor(Method);
  22:     }
  23:  
  24:     static void Main()
  25:     {
  26:         Console.WriteLine("{0,-10}{1,-12}{2}", "Times", "Reflection", "Expression");
  27:         Test(100000);
  28:         Test(1000000);
  29:         Test(10000000);
  30:     }
  31:  
  32:     private static void Test(int times)
  33:     {
  34:         Stopwatch stopwatch = new Stopwatch();
  35:  
  36:         stopwatch.Start();           
  37:         for (int i = 0; i < times; i++)
  38:         {
  39:             Method.Invoke(Target, args);
  40:         }
  41:         long elapsed1 = stopwatch.ElapsedMilliseconds;           
  42:  
  43:         stopwatch.Restart();
  44:         for (int i = 0; i < times; i++)
  45:         {
  46:             Executor(Target);
  47:         }
  48:         long elapsed2 = stopwatch.ElapsedMilliseconds;
  49:  
  50:         Console.WriteLine("{0,-10}{1,-12}{2}", times, elapsed1, elapsed2);
  51:     }
  52: }

測試方法Test的參數times表示我們執行目標方法的次數。在該方法中,我們調用MethodInfo對象的Invoke方法以反射的形式執行目標方法,然后利用Executor屬性表示的委托對象來執行目標方法,并將它們執行的時間(以毫秒為單位)輸出來。在作為程序入口的Main方法中,我們先后三個調用Test方法,并將執行目標方法的次數分別設置為100,000(十萬)、1,000,000(百萬)和10,000,000(千萬)。運行程序后我們會在控制臺上得到如下所示的輸出結果,可以看出直接采用反射方式執行某個方法確實在性能上要差一些,但是差異其實不算明顯。很多人總是覺得在程序中使用反射會對性能造成很大的影響,其實在我看來在很多情況下反射本身都不是造成性能瓶頸的元兇。

   1: Times      Reflection     Expression
   2: 100000     34             2
   3: 1000000    273            28
   4: 10000000   2627           284

文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()