Click here to Skip to main content
Click here to Skip to main content

A General Fast Method Invoker

By , 4 Jul 2006
 

Sample Image - FastMethodInvoker.gif

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Luyan
China China
Member
I am currently working for a .NET framework names AgileFramework. It's introduction at here:
http://www.agilelabs.cn/agileframework
 
Now I'm living in China. I have been designing and developing .NET based software applications for 5+ years.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionNot necessay at allmemberFatCatProgrammer22 Jan '13 - 8:25 
All you have to do is
1. Get the MethodInfo using reflection
2. Point a delegate to the methodInfo
3. invoke the delegate.
 
For faster performance just cache the delegate. That's it. Generating code has it's usage just not here!
Relativity

Questionabout your code's licensememberqsmy12322 Aug '12 - 15:00 
could you use your code with bsd license?
QuestionLicensememberSkeletor2323 Mar '12 - 5:05 
hi, can you please tell which license you use?
There's no license information attached?
QuestionConstructorInfo Support.memberMember 455125623 Jun '10 - 7:29 
I need something just like a fast method invoker, but instead of method invocation, i need to create instances of a runtimetype really fast.
 
Any ideas how to implement this?
 
public delegate object FastConstructorHandler(object[] paramters);
 
FastConstructorHandler GetConstructorInvoker(ConstructorInfo constructorInfo)
{
....
}
AnswerRe: ConstructorInfo Support.memberMember 455125623 Jun '10 - 9:00 
I tried to do it myself. Could some1 validate this, please?
The code uses some methods already defined in the FastMethodInvoker solution.
---------------------------
 
public static FastConstructorHandler CreateCtor(ConstructorInfo ctorInfo)
{
DynamicMethod dynamicMethod = new DynamicMethod(string.Empty,
typeof(object), new Type[] { typeof(object[]) }, ctorInfo.DeclaringType.Module, false);
 
ILGenerator il = dynamicMethod.GetILGenerator();
ParameterInfo[] ps = ctorInfo.GetParameters();

Type[] paramTypes = new Type[ps.Length];
LocalBuilder[] locals = new LocalBuilder[ps.Length];

for (int i = 0; i < paramTypes.Length; i++)
{
paramTypes[i] = ps[i].ParameterType;
locals[i] = il.DeclareLocal(paramTypes[i], true);
}
 
for (int i = 0; i < paramTypes.Length; i++)
{
il.Emit(OpCodes.Ldarg_0);
EmitFastInt(il, i);
il.Emit(OpCodes.Ldelem_Ref);
EmitCastToReference(il, paramTypes[i]);
il.Emit(OpCodes.Stloc, locals[i]);
}
 
for (int i = 0; i < paramTypes.Length; i++)
{
il.Emit(OpCodes.Ldloc, locals[i]);
}
 
il.Emit(OpCodes.Newobj, ctorInfo);
 
il.Emit(OpCodes.Ret);
return (FastConstructorHandler)dynamicMethod.CreateDelegate(typeof(FastConstructorHandler));
}
QuestionCan't work with 'struct' ?memberqmxle1 Oct '09 - 5:11 
Sorry, my English is poor. I'm a Chinese.
But if the target type is "struct", the result is incorrect.
Code:
 

class Program
{
static void Main(string[] args)
{
Type t = typeof(Person);
MethodInfo methodInfo = t.GetMethod("Say");
Person person = new Person(30);
methodInfo.Invoke(person, null);
FastInvokeHandler fastInvoker = GetMethodInvoker(methodInfo);
fastInvoker(person, null);
Console.ReadLine();
}
}
 
public struct Person
{
private readonly int _age;
 
public Person(int age)
{
_age = age;
}
 
public void Say()
{
Console.WriteLine("My age is {0}.", _age);
}
}
 

 

Result Is:
My age is 30.
My age is 1854584.
GeneralExcellent! Accessibility fix possible.memberblack198122 Jul '09 - 2:49 
Excellent, I am now using this in my project, and it smoothly replaced the default dynamic invocation. Great work.
 
One thing to note - there is way to bring this one step closer to the "default dynamic invocation", by disabling the accessibility checks (public, private etc.), since the methodInfo.Invoke does not consider them as well.
To disable them, add one more "bool" parameter to the constructor of the DynamicMethod, with value "true" (skipVisibility parameter).
QuestionPartial Trust questionmemberAndrey Belykh25 Apr '08 - 6:57 
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.
Generalprivate member supported and easier way with better performancememberyzh_x1 Jul '07 - 19:38 
// 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");

GeneralRe: private member supported and easier way with better performancememberLinguar2 Jul '07 - 9:15 
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);

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 4 Jul 2006
Article Copyright 2006 by Luyan
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid