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?
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.
Create Dictionary<MethodInfo, Action<object, object>> actions field in your class.
You can remove the static modifier from CreateSetAccess. And then add the following code in it:
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;
}
Related
I am trying to lower down reflection method call time by creating a DynamicMethod, emitting IL code with its IL generator, and then creating a delegate using its CreateDelegate method. So far the call times are reduced significantly even though the new method call still uses (object callObject, object[] params) as parameters.
The problem occurs in the generated IL code when the return type is anything other than void. I have no knowledge of MSIL and did quite a bit of research but found nothing. Here is the code that works:
private void Start()
{
var a = new A();
var method = typeof(A).GetMethod("Add");
Type[] paramsTypes = { typeof(object[]) };
Type[] allTypes = { typeof(object), typeof(object[]) };
Type returnType = typeof(void);
var dm = new DynamicMethod("Hi", returnType, allTypes);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, method, paramsTypes);
il.Emit(OpCodes.Ret);
var del4 = (Action<object, object[]>)dm.CreateDelegate(
typeof(Action<object, object[]>));
var time = DateTime.Now;
for (int i = 0; i < 20000000; i++)
{
//a.Add(); 132 ms
//method.Invoke(a, null);// 25 sec
del4(a, null); // 200 ms
}
var ts = DateTime.Now - time;
Debug.Log($"{ts.Seconds}:{ts.Milliseconds}");
}
public class A
{
public int a = 0;
public void Add() => a++;
}
The resulting time (only the call time is measured) is so much faster compared to normal MethodInfo.Invoke. However, with the following tweaks, the IL generator throws error:
private void Start()
{
var a = new A();
var method = typeof(A).GetMethod("Add");
Type[] paramsTypes = { typeof(object[]) };
Type[] allTypes = { typeof(object), typeof(object[]) };
Type returnType = typeof(object);
var dm = new DynamicMethod("Hi", returnType, allTypes);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, method, paramsTypes);
il.Emit(OpCodes.Ret);
var del4 = (Func<object, object[], object>)dm.CreateDelegate(
typeof(Func<object, object[], object>));
var time = DateTime.Now;
for (int i = 0; i < 20000000; i++)
{
//a.Add(); 132 ms
//method.Invoke(a, null);// 25 sec
del4(a, null); // 200 ms
}
var ts = DateTime.Now - time;
Debug.Log($"{ts.Seconds}:{ts.Milliseconds}");
}
public class A
{
public int a = 0;
public int Add() => a++;
}
When the return type expected is something other than void, the following exception is thrown:
InvalidProgramException: Invalid IL code in (wrapper dynamic-method) object:Hi (object,object[]): IL_0006: ret
Does anyone know how to solve this issue?
Edit: Added boxing before return opcode:
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, method, paramsTypes);
il.Emit(OpCodes.Box);
il.Emit(OpCodes.Ret);
Then the following is thrown:
VerificationException: Error in System.Object:(wrapper dynamic-method) object:Hi (object,object[]) Invalid instruction 8c
If I create a DynamicMethod from inside a class method how can I call another method of my class from the DynamicMethod Delegate? I need somehow to capture the this reference in the DynamicMethod code.
But I can't find an overladed version of ILGenerator.Emit which takes an object as parameter.
The code is so far:
void CallOpc(string name, object[] inp, object[] outp)
{
//...
}
public D CreateDelegate<D>(string opcName) where D : class
{
var dType = typeof(D);
var invoke = dType.GetMethod("Invoke");
var parameters = invoke.GetParameters();
var paramTypes = parameters.Select(p => p.ParameterType).ToArray();
DynamicMethod dm = new DynamicMethod(
opcName,
invoke.ReturnType,
paramTypes,
true);
var inp = parameters.Where(p => !p.IsOut).Select(p => p.ParameterType).ToList();
var outp = parameters.Where(p => p.IsOut).Select(p => p.ParameterType).ToList();
if (invoke.ReturnType != typeof(void))
{
outp.Insert(0, invoke.ReturnType);
}
ILGenerator il = dm.GetILGenerator();
LocalBuilder invar = il.DeclareLocal(typeof(object[]));
LocalBuilder outvar = il.DeclareLocal(typeof(object[]));
il.Emit(OpCodes.Ldc_I4, inp.Count);
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Stloc, invar);
for (int i = 0; i < inp.Count; i++)
{
il.Emit(OpCodes.Ldloc, invar);
il.Emit(OpCodes.Ldc_I4, i);
int j = Array.IndexOf(paramTypes, inp[i]);
il.Emit(OpCodes.Ldarg, j);
if (!inp[i].IsClass)
{
il.Emit(OpCodes.Box, inp[i]);
}
il.Emit(OpCodes.Stelem_Ref);
}
il.Emit(OpCodes.Ldc_I4, outp.Count);
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Stloc, outvar);
il.Emit(OpCodes.Ldarg_0); // <- push this on the evaluation stack ???
il.Emit(OpCodes.Ldstr, opcName);
il.Emit(OpCodes.Ldloc, invar);
il.Emit(OpCodes.Ldloc, outvar);
MethodInfo callOpcMeth = GetType().GetMethod("CallOpc", BindingFlags.Instance | BindingFlags.NonPublic);
il.Emit(OpCodes.Callvirt, callOpcMeth);
for (int o = 0; o < outp.Count; o++)
{
// TODO: handle out params and return value
}
il.Emit(OpCodes.Ret);
return (D)(object)dm.CreateDelegate(dType);
}
My problem is the line marked with ???
How to reference the this pointer from a DynamicMethod?
At low level, methods do not have a specific this pointer, instead the first argument of the method is used as this.
To reference the this pointer, here is what you have to do:
Prepend a new argument to your argument list, matching the type you want to use as this
Use that argument as this wherever you want
Create the delegate using DynamicMethod.CreateDelegate(Type,Object) to bind the first parameter to your object: return (D)(object)dm.CreateDelegate(dType, this);
Note that creating dynamic methods is expensive. If you generate the same DynamicMethod for multiple instances, you should cache the method itself, and create delegates using the cached method, but with different target parameter.
I have some class. And by reflection i get it constructor and parameters count/type.
also i have builder for any type.
so i need to make
var constructor;
var params = constructor.GetParameters();
object[] args;
foreach( var param in params ) {
var type = param.Parametertype;
args[] += (object)Build<type>();
}
Activator.CreateInstance(Type, args);
The problem, that i can not pass the type of parameter as generic argument.
No, you'll need to use reflection to call the generic method too:
var constructor = ...;
var parameters = constructor.GetParameters();
object[] args = new object[parameters.Length];
// Adjust this for private methods etc
var buildMethod = typeof(ClassContainingBuild).GetMethod("Build");
for (int i = 0; i < args.Length; i++)
{
var genericBuild = buildMethod.MakeGenericMethod(parameters[i].ParameterType);
// Adjust appropropriately for target etc
args[i] = genericBuild.Invoke(this, null);
}
I'm testing the performance of some similar method calls that I'm wrapping in some timing and logging statements. I'm passing these methods in via an Action delegate parameter.
Is there any way to print details about the call?
For example:
var httpResult = TestService(() => serviceHttp.Search(criteria));
var tcpResult = TestService(() => serviceTcp.Search(criteria));
var localResult = TestService(() => servicelocal.Search(criteria));
...
private static double TestService(Action serviceOperation)
{
const int iterations = 15;
...
for (var i = 0; i < iterations; i++)
{
var watch = Stopwatch.StartNew();
...
Console.WriteLine(string.Format("{0} ElapsedMilliseconds={1}", ????, watch.ElapsedMilliseconds));
// Ideally this would print something like "serviceTcp.DoStuff(...) ElapsedMilliseconds=313"
}
...
}
Change your testing method declaration to
private static double TestService(Expression<Action> expression)
Call Compile method of expression object to get a method for testing:
var serviceOperation = expression.Compile();
Expression object can provide a lot of information about method call, you can start with something like this:
private static string GetMethodCallDescription(Expression<Action> expression)
{
var mce = (MethodCallExpression)expression.Body;
var method = mce.Method;
var sb = new StringBuilder();
sb.Append(method.DeclaringType.Name);
sb.Append(".");
sb.Append(method.Name);
sb.Append("(");
bool firstarg = true;
foreach(var arg in mce.Arguments)
{
if(!firstarg)
{
sb.Append(", ");
}
else
{
firstarg = false;
}
sb.Append(arg.ToString());
}
sb.Append(")");
return sb.ToString();
}
You could do it without using expression trees; just change the signature of TestService to take the action and the parameter separately, and use the Delegate.Target and Delegate.Method properties to get the type and method:
var httpResult = TestService(serviceHttp.Search, criteria);
var tcpResult = TestService(serviceTcp.Search, criteria);
var localResult = TestService(servicelocal.Search, criteria);
...
private static double TestService<T>(Action<T> serviceOperation, T parameter)
{
const int iterations = 15;
...
for (var i = 0; i < iterations; i++)
{
var watch = Stopwatch.StartNew();
...
string typeName = serviceOperation.Method.IsStatic
? serviceOperation.Method.DeclaringType.Name
: serviceOperation.Target.GetType().Name;
string methodName = serviceOperation.Method.Name;
Console.WriteLine(string.Format("{0}.{1} ElapsedMilliseconds={2}", typeName, methodName, watch.ElapsedMilliseconds));
// Ideally this would print something like "serviceTcp.DoStuff(...) ElapsedMilliseconds=313"
}
...
}
How to get the ParameterInfo of a function with variable number of params?
The problem is when I call the method
MyFunction(object o1, out object o2);
I can get the parameterInfo of sendData but not the o1 and o2 object.
protected object[] MyFunction(params object[] sendData)
{
StackTrace callStack = new StackTrace(0, false);
StackFrame callingMethodFrame = callStack.GetFrame(0);
MethodBase callingMethod = callingMethodFrame.GetMethod();
ParameterInfo[] parametersInfo = callingMethod.GetParameters();
List<object> inParams = new List<object>();
List<object> outParams = new List<object>();
for (int i = 0; i < sendData.Length; i++)
{
object value = sendData[i];
ParameterInfo info = parametersInfo[parametersInfo.Length - sendData.Length + i];
if (info.IsOut)
{
outParams.Add(value);
}
else
{
inParams.Add(value);
}
}
..........
}
Thanks in advance for helping me.
Arnaud
'params' is just C# syntactic sugar. In fact, at metadata .NET level, there is only one parameter named "sendData" with a specific "ParamArray" attribute set.