DevEstacion DevEstacion - 1 month ago 25
C# Question

Compiled Expression slower than Reflection

I have a PropertyInfo.SetValue that has a dynamic set. Meaning the value to be set is not known.

I've have a method like this i got from the internet.

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();
}


What this does is create a expression and compile it, but the objects get converted using the parameter types.

I consume it like this.

var method2 = CreateSetAccess(property.GetSetMethod());
method2(response, valueToSet);


What happens is that this seems to be slower that the
PropertyInfo.SetValue


Here is my benchmark

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}
}


I'm wondering why this happens? I'm guessing because I'm always creating a new expression, but how can i make it not create a new object and make it get cached?

usr usr
Answer

If compiling a fresh expression each time was faster then the Reflection API would just do this internally. Therefore, it is not. This technique only works if you reuse the same compiled code many times.

so there is no way to make the expression adjust at runtime based on the methodinfo supplied?

Reflection does this and that's what's making it so slow. Keep a cache of compiled methods. For example in a Dictionary<MethodInfo, Action<object, object>> with a suitable comparer.