|

Introduction
Sometimes, I run across the need to dynamically invoke the method of an object, where the actual method might not be known until run-time. Usually, Reflecting is nice, but frequently doing it can be too slow. This article describes an alternative method for dynamic method invocation.
Background
When I read the article Fast Dynamic Property Accessors, I was thinking about my project, it has a lots of reflecting methods in circle. But it's methods not properties. But the DynamicMethod reminded me, maybe I could use Emit to generate a DynamicMethod to bind a special method before it can be invoked. I hope it will improve performance.
Using the Code
First, I reflected out the method which will be invoked: MethodInfo methodInfo = typeof(Person).GetMethod("Say");
Then, I get the MethodInvoker to invoke: FastInvokeHandler fastInvoker = GetMethodInvoker(methodInfo);
fastInvoker(new Person(), new object[]{"hello"});
Instead of using reflection method, invoke in the past: methodInfo.Invoke(new Person(), new object[]{"hello"});
Implementation
First, I need to define a delegate to adapt the dynamic method: public delegate object FastInvokeHandler(object target,
object[] paramters);
It looks the same as the class MethodInfo's Invoke method. Yes, that means I can write the same code to use it like in the past.
This code generates the DynamicMethod: public static FastInvokeHandler GetMethodInvoker(MethodInfo methodInfo)
{
DynamicMethod dynamicMethod = new DynamicMethod(string.Empty,
typeof(object), new Type[] { typeof(object),
typeof(object[]) },
methodInfo.DeclaringType.Module);
ILGenerator il = dynamicMethod.GetILGenerator();
ParameterInfo[] ps = methodInfo.GetParameters();
Type[] paramTypes = new Type[ps.Length];
for (int i = 0; i < paramTypes.Length; i++)
{
paramTypes[i] = ps[i].ParameterType;
}
LocalBuilder[] locals = new LocalBuilder[paramTypes.Length];
for (int i = 0; i < paramTypes.Length; i++)
{
locals[i] = il.DeclareLocal(paramTypes[i]);
}
for (int i = 0; i < paramTypes.Length; i++)
{
il.Emit(OpCodes.Ldarg_1);
EmitFastInt(il, i);
il.Emit(OpCodes.Ldelem_Ref);
EmitCastToReference(il, paramTypes[i]);
il.Emit(OpCodes.Stloc, locals[i]);
}
il.Emit(OpCodes.Ldarg_0);
for (int i = 0; i < paramTypes.Length; i++)
{
il.Emit(OpCodes.Ldloc, locals[i]);
}
il.EmitCall(OpCodes.Call, methodInfo, null);
if (methodInfo.ReturnType == typeof(void))
il.Emit(OpCodes.Ldnull);
else
EmitBoxIfNeeded(il, methodInfo.ReturnType);
il.Emit(OpCodes.Ret);
FastInvokeHandler invoder =
(FastInvokeHandler)dynamicMethod.CreateDelegate(
typeof(FastInvokeHandler));
return invoder;
}
Conclusion
Well, I think this is a general way that can be used instead of most of the reflection methods to get about 50 times performance improvement. Any suggestions for improvements are welcome.
Extra advantage (reminded by MaxGuernsey): If an exception occurs in your code, FastInovker would throw the original one, but the Method.Invoke would throw a TargetInvocationException.
History
- 2006-7-05: Updated to add static method support. Thanks Manuel Abadia.
- 2006-6-30: Updated to add
ref/out parameter support. Thanks Roger for his nice suggestion.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 61 (Total in Forum: 61) (Refresh) | FirstPrevNext |
|
|
 |
|
|
Thanks for your article. Do you know how to make it work in Partial Trust environment?
DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module);
...needs "Reflection.Emit" permission , if I do this:
DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) } );
...the permission is not required, however the Invoke throws System.Security.VerificationException {"Operation could destabilize the runtime."}.
I would appreciate any ideas.
Thanks again for the great article.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
// T is delegate, methodInfo can be private static T GetDelegate(object instance, MethodInfo methodInfo) where T : class { return Delegate.CreateDelegate(typeof(T), instance, methodInfo) as T; }
call it like this: delegate void SayDelegate(ref string word, out Person p, int avi); ... Person person = new Person(); MethodInfo methodInfo = t.GetMethod("Say"); ... SayDelegate sayDelegate = GetDelegate(person, methodInfo); Stopwatch watch3 = new Stopwatch(); watch3.Start(); for (int i = 0; i < 1000000; i++) { sayDelegate(ref word, out p, 3); } watch3.Stop(); Console.WriteLine("1000000 times invoked: " + watch3.ElapsedMilliseconds + "ms");
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
Congratulations on finding a way to obfuscate standard calling conventions, and defeating the point of delegates all together.
Sure that would be an effective means to obtain it were the method private, and that's what you'd do if you -knew the signature-. That's the difference here.
The FastMethodInvoke is goal oriented at solving the problem of what you do when you -don't- know the signature of the method. You'd basically use it for a dynamic application that binds to methods indiscriminately. I use it for creating a fast constructor invoking system. Sure it only allows public members, but you rarely need a case where you invoke private members, if it's not your member you're invoking, that's fine, but if it's your own architecture you should be able to design it in a way where such methods aren't needed.
The same thing you have as above can be done like so, if Say is public: SayDelegate sd = new SayDelegate(new Person().Say); Stopwatch sw = new Stopwatch(); sw.Start(); Person p = null; string s = "Test"; for (int i = 0; i < 1000000; i++) sd(ref s, out p, 0); sw.Stop(); Console.WriteLine(sw.Elapsed);
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
You are right, the signature is required to know. But, there is small different, the method "say" can be assigned at runtime, not compile-time.
And this way has a fatal weakness, if the method is instance method and require to change the instance many times, this way will take much time. ( the following Delegate method takes long time. [MethodImpl(MethodImplOptions.InternalCall)] private extern bool BindToMethodInfo(object target, RuntimeMethodHandle method, RuntimeTypeHandle methodType, DelegateBindingFlags flags); )
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
You're using methods I don't really use. I never bound the method directly to a specific instance, which is why it required two arguments: one for the instance to refer to on the call, and two, for the parameters to make the call.
The FastMethodInvoker basically emits the object instance (Ldarg_0) and then the series of parameters (Ldarg_1 and then the members of the array are cast into new local values of the proper type, which are then pushed onto the stack.)
After the object and members are pushed to the stack you just call the method with the information provided (Callvirt). You can access private members if, rather than using the module of the type, you use the -DeclaringType- in creating the dynamic method. This associates the dynamic method to the scope of the type in general. So basically both methods have private method access (I stumbled on that by random chance.)
The FastMethodInvoker, by design, literally creates a new object-parameter list method on the fly. Which, as I'm sure you know by looking, there will be a slight hit in speed due to the copying of the parameters into local copies, but that's necessary since you're calling a method using an object array, you have to ensure the values are properly cast/unboxed before making the call.
I use a variant of this system on an inheritance constructor validation system. That is, because constructors, unlike all other (visible) members, do not inherit normally, ensuring derivations of a known design have the constructors needed to instantiate the inherited type(s) is indeed a chore (at a minimum using ugly reflection and checks specific to the individual case.) After verifying the constructor(s) exist(s) I can use this system to create a dynamic method that will instantiate the specific type(s) quickly. The reason I don't just use a simpler system like yours that doesn't emit the duplicate parameter:local listing is the specific signatures are defined through the root-type's constructor attributes. So the signatures would change case-by-case, depending on the specific needs of whoever uses it.
But I guess everyone has their own personal uses for things like this. It's select code that very few people would need. Edit: And on top of it all, I added parameter-count checks, primitive up-casting checks (if it requires a double for the parameter, a byte is fine since it's in the range of values, it now casts properly). Something this example lacked.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Excellent article = thank you. And, also thanks to Manuel Abadia[^] for his extensions to it.
I have extendend the work a little further, implementing a complete generic late-bound wrapper for a utilising these techniques. It allows for late binding of the default constructor, parameterised constructors, and both static and instance methods and properties. It automatically caches the generated IL (in static dictionaries, so it is avalable for all instances) when it is first called. Usage is like this:
// Create an instance Latebound<MyType> myType = new Latebound<MyType>(optional parameters); // Access a property myType["MyProperty"] = value; value = myType["MyOtherProperty"]; // Call a method myType.Call("MyMethod", optional parameters);
// Access a Static property Latebound<MyType>.SetStatic("MyStaticProperty", value); value = Latebound<MyType>.GetStatic("MyStaticProperty");
// Call A Static Method value = Latebound<MyType>.CallStatic("MyStaticMethod", optional parameters);
The source for this class is available Here[^]
Regards
Mick http://midimick.com/magicknet/magickDoc.html[^]
|
| Sign In·View Thread·PermaLink | 1.80/5 (2 votes) |
|
|
|
 |
|
|
I'm no huge freak over comments or structure, but there's practically no definition of what's happening in the method that does everything...
If you're interested, I've rewritten it into my preferred method, and it can be found here: Optimized Method Call
Later,
-Allen Copeland Jr.
PS: I'm no IL guru, so the comments could be complete nonsense, let me know?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hi Luyan,
Thanks much for your project. It was extremely helpful in some development work that I have recently done. I decided that I would send you a little wrapper class, and a test project that I did using your code. I couldn't quite find an email address for you, so I posted it on code project (FastInvokeWrapper.asp).
Of course, I botched up the links, so it's going to take a few days before it get's properly posted. Anyway, thanks again for your project.
Billy p.
|
| Sign In·View Thread·PermaLink | 1.50/5 (3 votes) |
|
|
|
 |
|
|
I think the result is not exact. Before we invoke the target method,we should do preparetion to create the instance of target class,and get the MethodInfo of the target method. So, I think we should add them to the test. I think we should test the following three method:
private static void Reflection() { string word = "hello"; Person p = null; object[] param = new object[] { word, p, 3 }; Type tp = typeof(Person); object person = new Person(); MethodInfo methodInfo = tp.GetMethod("Say"); methodInfo.Invoke(person, param); }
private static void FastInvoke() { string word = "hello"; Person p = null; Type t = typeof(Person); object[] param = new object[] { word, p, 3 }; MethodInfo methodInfo = t.GetMethod("Say"); FastInvokeHandler fastInvoker = GetMethodInvoker(methodInfo); Person person = new Person(); fastInvoker(person, param); }
private static void DirectCall() { string word = "hello"; Person p = null; Person person = new Person(); person.Say(ref word,out p, 3); }
|
| Sign In·View Thread·PermaLink | 2.60/5 (2 votes) |
|
|
|
 |
|
|
I'm agree with it. When I modify the test as follows
Stopwatch watch = new Stopwatch(); watch.Start(); for (int i = 0; i < 1000; i++) { Type t = typeof(Person); MethodInfo methodInfo = t.GetMethod("Say"); Person person = new Person(); string word = "hello"; Person p = null; object[] param = new object[] { word, p, 3 }; methodInfo.Invoke(person, param); } watch.Stop(); Console.WriteLine("1000 times invoked by Reflection: " + watch.ElapsedMilliseconds + "ms");
Stopwatch watch1 = new Stopwatch(); watch1.Start(); for (int i = 0; i < 1000; i++) { Type t = typeof(Person); MethodInfo methodInfo = t.GetMethod("Say"); Person person = new Person(); string word = "hello"; Person p = null; object[] param = new object[] { word, p, 3 }; FastInvokeHandler fastInvoker = GetMethodInvoker(methodInfo); fastInvoker(person, param); } watch1.Stop(); Console.WriteLine("1000 times invoked by FastInvoke: " + watch1.ElapsedMilliseconds + "ms");
Stopwatch watch2 = new Stopwatch(); watch2.Start(); for (int i = 0; i < 1000; i++) { Person person = new Person(); string word = "hello"; Person p = null; person.Say(ref word, out p, 3); } watch2.Stop(); Console.WriteLine("1000 times invoked by DirectCall: " + watch2.ElapsedMilliseconds + "ms");
The result is 18ms 542ms 0ms We can see before we can fast call the method,we already lose much more time to generate the instance. Any suggestion about it?
|
| Sign In·View Thread·PermaLink | 5.00/5 (2 votes) |
|
|
|
 |
|
|
That's true. If you call the method just ONE time, fastMethodInvoker is indeed slower than reflection. But if the method is called many times, you can buffer the FastInvokeHandler in a collection(Dictionary, etc.) at the first call for a better performance later.
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
I get MethodAccessException while using get properties on reference type generics. I works succesfully with value type generics however i couldn't find a solution for this. I also checked ildasm utility and the code generated looks like the same. Do you have any solution for this ?
Generated IL Code : IL_0000: ldarg.0 IL_0001: callvirt System.String get_Value2()/FastMethodInvoker.aField`1[System.String] IL_0006: ret
Code:
public class aField { private T m_Value; public T Value2 { get { return m_Value; } set { m_Value = value; } } } static void Main(string[] args) { aField s = new aField(); s.Value2 = "test";
aField p = new aField(); p.Value2 = 5;
Type t = s.GetType();
MethodInfo methodInfo = t.GetProperty("Value2").GetGetMethod(); }
Can Erten
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I found the solution from a previous post. On the dynamic method constructor we need to set skipVisibility to true.
DynamicMethod dm = new DynamicMethod("GetPropertyorField_" + methodName, typeof(object), new Type[] { typeof(object) },mod,true);
However I didn't understand the cause of that since the property is public. Also it works on aField<int> and is not working with aField<string> without modifying the dynamicmethod contructor. Any idea about the cause ?
Can Erten
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
Good article. I have a 2.6G P4 machine with 1G of RAM .
When i run you demo in release mode i get figures of 4050, 488 and 406 ms
Can anyone explain the vast difference in times to the posted article ?
Many thanks
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I'm no expert at this, however I have a feeling it's likely due to the fact of the different processor types. If the source person was using an AMD, and there are things it would perform better at; perhaps this is one. That's assuming the processor core is different. If it's not, it's likely due to other hardware related matters, processor load at the individual test cycles, and many other factors.
It's difficult to just give numbers out and say 'this is why, my machine's faster'. That doesn't work in this case because his 40 ms is 10x better then yours, but his other is only ~1.5 times better. So it's one that wouldn't be so easily figured.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
 |
|
|
Hi Luyan, I am fairly new to .NET (using it for about a year). The article is intriguiging and seems like it can be really helpful to use. Do you know where I can find some basic info on this topic so I can make use of your code.. Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hi, ...you got a 5 also from me!
A question about a possible extension: how is it possible to call a private member? I've tried to get MethodInfo with BindingFlags but all I can see is an exception.
I modified the "Run" method of Person class as private and than I used:
Type t = typeof(Person); MethodInfo methodInfo = t.GetMethod("Run", BindingFlags.NonPublic | BindingFlags.Instance); ...
Exception is "MethodAccessException": System.MethodAccessException was unhandled Message="Test.Person.Run(System.String ByRef, Test.Person ByRef, Int32)" Source="FastMethodInvoker" ...
Any idea/suggestion?
Regards, mauro
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
To call a private member you need to make sure that the last parameter of the DynamicMethod constructor is a Type that has access to the private member, i.e. the Type of the declaring class.
Try changing this:
DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType.Module);
To this:
DynamicMethod dynamicMethod = new DynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, methodInfo.DeclaringType);
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thank you for your precious help, it works!
I didn't think it was a mandatory requisite, because using reflection I do not have to refer to a type/module with visibility on the hidden method. Whit the following code I'm able to execute private methods on types of other namespaces loaded at runtime:
oBaseObject.GetType().UnderlyingSystemType.InvokeMember( "Methodname", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, oBaseObject, oArguments);
However, problem solved!
Bye, Mauro
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|