2014-04-02 121 views
2

我正在嘗試優化使用反射創建各種視圖的傳統代碼中類的性能。我寧願我們根本沒有使用反射,但在短期內消除它並不是一種選擇。代碼來自MVC#框架。這裏是:使用反射優化對象創建

public class CreateHelper 
{ 
    #region Documentation 
    /// <summary> 
    /// Creates an object of specified type. 
    /// </summary> 
    #endregion 
    public static object Create(Type t) 
    { 
     return t.GetConstructor(new Type[] { }).Invoke(new object[] { }); 
    } 

    #region Documentation 
    /// <summary> 
    /// Creates an object of specified type with parameters passed to the constructor. 
    /// </summary> 
    #endregion 
    public static object Create(Type t, params object[] parameters) 
    { 
     Type[] paramTypes = new Type[parameters.Length]; 
     for (int i = 0; i < parameters.Length; i++) 
      paramTypes[i] = parameters[i].GetType(); 
     return t.GetConstructor(paramTypes).Invoke(parameters); 
    } 
} 

我期待儘快實現這兩種方法。我讀了這個關於對象創建優化的great article by Ayende,並試圖修改他的例子來達到我的目的,但是我對IL的知識並不存在。

我得到一個VerificationException操作可能會破壞運行時。Create方法中。有誰知道這是什麼問題?有沒有更快的實施這種方法我可以使用?這裏是我的嘗試:

public class Created 
{ 
    public int Num; 
    public string Name; 

    public Created() 
    { 
    } 

    public Created(int num, string name) 
    { 
     this.Num = num; 
     this.Name = name; 
    } 
} 

public class CreateHelper 
{ 
    private delegate object CreateCtor(); 
    private static CreateCtor createdCtorDelegate; 

    #region Documentation 
    /// <summary> 
    /// Creates an object of specified type. 
    /// </summary> 
    #endregion 
    public static object Create(Type t) 
    { 
     var ctor = t.GetConstructor(new Type[] { }); 

     var method = new DynamicMethod("CreateIntance", t, new Type[] { typeof(object[]) }); 
     var gen = method.GetILGenerator(); 
     gen.Emit(OpCodes.Ldarg_0);//arr 
     gen.Emit(OpCodes.Call, ctor);// new Created 
     gen.Emit(OpCodes.Ret); 
     createdCtorDelegate = (CreateCtor)method.CreateDelegate(typeof(CreateCtor)); 
     return createdCtorDelegate(); // <=== VerificationException Operation could destabilize the runtime. 
    } 

    #region Documentation 
    /// <summary> 
    /// Creates an object of specified type with parameters passed to the constructor. 
    /// </summary> 
    #endregion 
    public static object Create(Type t, params object[] parameters) 
    { 
     Type[] paramTypes = new Type[parameters.Length]; 
     for (int i = 0; i < parameters.Length; i++) 
      paramTypes[i] = parameters[i].GetType(); 
     return t.GetConstructor(paramTypes).Invoke(parameters); 
    } 
} 

我那麼使用類是這樣的:

class Program 
{ 
    private static Created CreateInstance() 
    { 
     return (Created)CreateHelper.Create(typeof(Created)); 
     //return new Created(); 
    } 

    static void Main(string[] args) 
    { 
     int iterations = 1000000; 
     Stopwatch watch = Stopwatch.StartNew(); 
     for (int i = 0; i < iterations; i++) 
     { 
      CreateInstance(); 
     } 
     Console.WriteLine(watch.Elapsed); 

     Console.ReadLine(); 
    } 
} 

更新1

我做了一些計時:

  • new Created():00: 00:00.0225015
  • Activator.CreateInstance<Created>():00:00:00.1232143
  • (Created)CreateHelper.Create(typeof(Created)):00:00:00.3946555

  • new Created(i, i.ToString()):00:00:00.1476882

  • (Created)Activator.CreateInstance(typeof(Created), new object[]{ i, i.ToString() }):00:00:01.6342624
  • (Created)CreateHelper.Create(typeof(Created), new object[] {i, i.ToString()}):00:00 :01.1591511

更新2

對於默認的構造函數情況,@Brannon建議的解決方案工作,但獲得的時間是00:00:00.1165000,這並不是一個巨大的改進。那就是:

public class CreateHelper 
{ 
    private delegate object DefaultConstructor(); 

    private static readonly ConcurrentDictionary<Type, DefaultConstructor> DefaultConstructors = new ConcurrentDictionary<Type, DefaultConstructor>(); 

    #region Documentation 
    /// <summary> 
    /// Creates an object of specified type. 
    /// </summary> 
    #endregion 
    public static object Create(Type t) 
    { 
     DefaultConstructor defaultConstructorDelegate; 

     if (!DefaultConstructors.TryGetValue(t, out defaultConstructorDelegate)) 
     { 
      var ctor = t.GetConstructor(Type.EmptyTypes); 

      var method = new DynamicMethod("CreateIntance", t, Type.EmptyTypes); 
      var gen = method.GetILGenerator(); 
      gen.Emit(OpCodes.Nop); 
      gen.Emit(OpCodes.Newobj, ctor); 
      gen.Emit(OpCodes.Ret); 
      defaultConstructorDelegate = (DefaultConstructor)method.CreateDelegate(typeof(DefaultConstructor)); 
      DefaultConstructors[t] = defaultConstructorDelegate; 
     } 

     return defaultConstructorDelegate.Invoke(); 
    } 
} 

更新3

使用的Expression.New編譯表情也取得了很不錯的成績(00:00:00.1166022)。下面是代碼:

public class CreateHelper 
{   
    private static readonly ConcurrentDictionary<Type, Func<object>> DefaultConstructors = new ConcurrentDictionary<Type, Func<object>>(); 

    #region Documentation 
    /// <summary> 
    /// Creates an object of specified type. 
    /// </summary> 
    #endregion 
    public static object Create(Type t) 
    { 
     Func<object> defaultConstructor; 

     if (!DefaultConstructors.TryGetValue(t, out defaultConstructor)) 
     { 
      var ctor = t.GetConstructor(Type.EmptyTypes); 

      if (ctor == null) 
      { 
       throw new ArgumentException("Unsupported constructor for type " + t); 
      } 

      var constructorExpression = Expression.New(ctor); 
      var lambda = Expression.Lambda<Func<Created>>(constructorExpression); 
      defaultConstructor = lambda.Compile(); 
      DefaultConstructors[t] = defaultConstructor; 
     } 

     return defaultConstructor.Invoke(); 
    } 

    #region Documentation 
    /// <summary> 
    /// Creates an object of specified type with parameters passed to the constructor. 
    /// </summary> 
    #endregion 
    public static object Create(Type t, params object[] parameters) 
    { 
     return null; 
    } 
} 

摘要

對於默認的構造函數的情況下,這裏是概要:

  • (Created)CreateHelper.Create(typeof(Created)):00:00:00.3946555
  • new Created():00: 00:00.0225015
  • Activator.CreateInstance<Created>():00:00:00。1232143
  • DynamicMethod:00:00:00.1165000
  • Expression.New:00:00:00.1131143
+0

Activator.CreateInstance – hazzik

+0

很好的建議,但仍比使用構造函數慢,但快4倍,比原來的implementaiton –

+0

六次你也應該嘗試'Expression.New':http://geekswithblogs.net/ mrsteve/archive/2012/02/19/a-fast-c-sharp-extension-method-using-expression-trees-create-instance-from-type-again.aspx – YK1

回答

1

首先,你應該禁用特定測試的垃圾收集器。這可能會妨礙你的結果。或者你可以把所有創建的實例放入一個數組中。我不知道作爲它的一部分調用ToString()也有幫助。

您對花式構造函數代碼的計劃不一定是正確的計劃。構造函數查找本身很慢。您應該使用Type密鑰將代表緩存在字典中。大多數IoC容器會自動爲您執行此操作(緩存和構建)。我認爲使用其中之一在您的情況下將證明是有價值的。實際上,較新的JSON框架還會緩存構造函數信息以快速創建對象。也許類似Json.Net或ServiceStack.Text會有所幫助。

您構建了多少種不同的類型? (我知道你的例子只顯示一個。)

我不確定你的DynamicMethod參數是否正確。這個代碼(下面)對我來說一直很穩定。不要在值類型或數組上調用它。

DynamicMethod dm = new DynamicMethod("MyCtor", type, Type.EmptyTypes, typeof(ClassFactory).Module, true); 
ILGenerator ilgen = dm.GetILGenerator(); 
ilgen.Emit(OpCodes.Nop); 
ilgen.Emit(OpCodes.Newobj, ci); 
ilgen.Emit(OpCodes.Ret); 
CtorDelegate del = ((CtorDelegate) dm.CreateDelegate(typeof(CtorDelegate))); 
return del.Invoke(); // could cache del in a dictionary 
+0

+1謝謝你的回答,我會嘗試你的代碼並將構造函數委託存儲在字典中。我們可能創造了大約50種不同的類型。 –

+0

您發佈的代碼工作得很好,謝謝!你有沒有機會知道如何爲非默認構造函數生成IL? –

+0

我不知道其他構造函數的IL。對於他們,你可以嘗試緩存編譯的表達式。看到這裏:http://rogeralsing.com/2008/02/28/linq-expressions-creating-objects/ – Brannon