我正在研究動態實例化類的自動化。
我決定編寫一個生成Func
的表達式樹,可以為我實例化我的類。但是,我注意到我的Func
性能降低了3倍,而不是簡單地使用new
。
根據我對錶達樹和調用函數的了解,性能差異應該幾乎不存在(可能是20-30%,但遠不及慢3倍)
首先,這是我正在建立的表達
public Expression<Func<A1, T>> BuildLambda<T, A1>(string param1Name)
{
var createdType = typeof(T);
var param = Expression.Parameter(typeof(A1), param1Name);
var ctor = Expression.New(createdType);
var prop = createdType.GetProperty(param1Name);
var displayValueAssignment = Expression.Bind(prop, param);
var memberInit = Expression.MemberInit(ctor, displayValueAssignment);
return
Expression.Lambda<Func<A1, T>>(memberInit, param);
}
然後我繼續編譯它(我只做一次)
var c1 = mapper.BuildLambda<Class1, int>("Id").Compile();
然後我像這樣調用我的Func
var result = c1.Invoke(5);
當我把這最後一部分放在一個循環中並將它與類似的東西進行比較時
var result = new Class1() { Id = 5 };
我做了幾個測試,比較兩者的性能,這就是我最終得到的結果:
100,000 Iterations - new: 0ms. | Func 2ms.
600,000 Iterations - new: 5ms. | Func 14ms.
3,100,000 Iterations - new: 24ms. | Func 74ms.
15,600,000 Iterations - new: 118ms. | Func 378ms.
78,100,000 Iterations - new: 597ms. | Func 1767ms.
正如您所看到的,我的Func.Invoke()
大約比使用new
實例化慢2.5到3倍 。有沒有人有任何關於如何改進這個的提示? (我不介意使用純反射,因為我設法獲得更好的性能)
*對於任何想要在此測試的人來說,我的設置是一個垃圾箱: https : //pastebin.com/yvMLqZ2t
閱讀完評論中的所有帖子後,我想出了這個想法:當你創建一個DynamicMethod
而不是表達式樹並將它邏輯地分配給當前執行代碼的模塊時,你不應該得到這個開銷。
我認為(或者至少希望)你正在尋找關於總體思路的改進選項,而不是基於表達式樹的版本,所以我將其作為改進選項發布:)
所以我嘗試了這段代碼:
public static Func<A1, T> BuildLambda<A1, T>(string propertyName)
{
// This is where the magic happens with the last parameter!!
DynamicMethod dm = new DynamicMethod("Create", typeof(T), new Type[] { typeof(A1) }, typeof(Program).Module);
// Everything else is just generating IL-code at runtime to create the class and set the property
var setter = typeof(T).GetProperty(propertyName).SetMethod;
var generator = dm.GetILGenerator();
var local = generator.DeclareLocal(typeof(T));
generator.Emit(OpCodes.Newobj, typeof(Class1).GetConstructor(Type.EmptyTypes));
generator.Emit(OpCodes.Stloc, local);
generator.Emit(OpCodes.Ldloc, local);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Call, setter);
generator.Emit(OpCodes.Ldloc, local);
generator.Emit(OpCodes.Ret);
return (Func<A1, T>)dm.CreateDelegate(typeof(Func<A1, T>));
}
在我的機器上,這個生成的委託最多比手寫代碼慢1.8倍, 而沒有指定屬性 。不是1.5,但至少我不必在我的代碼中包含一個程序集範圍的屬性,我不完全理解:)
請注意,如果省略DynamicMethod
構造函數的最後一個參數,則生成的代碼仍然會得到更慢的結果。
編輯
我偶然發現了這篇博文,它提出了同樣的問題並給出了相同的解決方案: