[英]Why is code compiled with Expression<TDelegate>.Compile() slower than plain C#?
[英]Compiled Expression slower than Reflection
我有一個具有動態設置的PropertyInfo.SetValue。 含義要設置的值未知。
我有從互聯網上獲得的類似方法。
private static Action<object, object> CreateSetAccess(MethodInfo method)
{
var obj = Expression.Parameter(typeof(object), "o");
var value = Expression.Parameter(typeof(object));
Expression<Action<object, object>> expr =
Expression.Lambda<Action<object, object>>(
Expression.Call(
Expression.Convert(obj, method.DeclaringType),
method,
Expression.Convert(value, method.GetParameters()[0].ParameterType)),
obj,
value);
return expr.Compile();
}
這樣做是創建一個表達式並將其編譯,但是對象將使用參數類型進行轉換。
我這樣消費。
var method2 = CreateSetAccess(property.GetSetMethod());
method2(response, valueToSet);
發生的情況是,這似乎比PropertyInfo.SetValue
慢
這是我的基准
var xpathNavigator = XmlHelper.CreateXPathDocument(serviceResponse).CreateNavigator();
foreach (var propertyInformation in propertyInformationSource)
{
// Gets the node using the NodePath provided in the Attribute
var attr = propertyInformation.Value;
var pathValue = xpathNavigator.SelectSingleNode(attr.NodePath);
if (pathValue == null)
continue;
object valueToSet = null;
var property = propertyInformation.Key;
if (propertyInformation.Value.ShouldDeserialize)
valueToSet = serializationHelper.Deserialize(property.PropertyType, pathValue.OuterXml, attr.CustomRoot);
else
valueToSet = Convert.ChangeType(pathValue.Value, property.PropertyType);
// this line is only added for the testing for it to be JITd
var method = CreateSetAccess(property.GetSetMethod());
method(response, valueToSet);
property.SetValue(response, valueToSet);
// end
TimeSpan fastSet, setValue;
const int COUNT = 100000;
var watch = Stopwatch.StartNew();
for (int i = 0; i < COUNT; i++)
{
var method2 = CreateSetAccess(property.GetSetMethod());
method2(response, valueToSet);
}
watch.Stop();
fastSet = watch.Elapsed; // result {00:00:08.8500760}
watch = Stopwatch.StartNew();
for (int i = 0; i < COUNT; i++)
{
property.SetValue(response, valueToSet);
}
watch.Stop();
setValue = watch.Elapsed; // result {00:00:00.0263953}
}
我想知道為什么會這樣嗎? 我猜是因為我總是在創建一個新的表達式,但是如何使它不創建新的對象並使它被緩存呢?
如果每次編譯一個新的表達式都更快,那么Reflection API就會在內部完成。 因此,事實並非如此。 僅當您多次重復使用相同的已編譯代碼時,此技術才有效。
所以沒有辦法根據提供的methodinfo在運行時調整表達式?
反射可以做到這一點,這就是它讓它變得如此緩慢的原因。 保留已編譯方法的緩存。 例如,在具有適當比較器的Dictionary<MethodInfo, Action<object, object>>
。
在您的類中創建Dictionary<MethodInfo, Action<object, object>> actions
字段。 您可以從CreateSetAccess中刪除靜態修飾符。 然后在其中添加以下代碼:
Action<object, object> action = null;
if(actions.TryGetValue(method, out action))
{
return action;
}
var obj = Expression.Parameter(typeof(object), "o");
var value = Expression.Parameter(typeof(object));
Expression<Action<object, object>> expr =
Expression.Lambda<Action<object, object>>(
Expression.Call(
Expression.Convert(obj, method.DeclaringType),
method,
Expression.Convert(value, method.GetParameters()[0].ParameterType)),
obj,
value);
expr.Compile();
cache.Add(method, expr);
return expr;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.