More late-bound invocation scenario notes

There are various other scenario’s in the invocation space that have not been dissected. I mentioned a few of them in the comments section of one of my other posts. To be a little more illustrative, this posting will iterate over some of these scenario’s, and provide notes for each. It’s worthwhile to remember, this is simply a braindump, and until we’ve firmed up the story for Whidbey, I wouldn’t recommend you take this as “written in stone”.

 

Some late bound invocation scenario’s may include:

 

•           Known method signature (parameters), unknown method name and method type

•           Known signature and known method name, unknown receiver

•           Known name

 

I’ll be talking about the first two for these particular notes.

 

1. Known method signature (parameters), unknown method name and method type

 

This scenario is presented by an extensibility mechanism where the user specifies a particular custom attribute on a method with the given signature.  At runtime, the methods with the custom attribute are retrieved and thus the callback is available. In this scenario, the arguments are typically provided by the code invoking the callback (the source) so there is usually no coercion/conversion that needs to happen before hand. There is generally nothing unknown at the point of invocation.

 

Invocation through reflection is very heavy for such scenario’s, and a delegate is the right solution. Reflection invocation is heavy because there is a single entry point, so it’s forced to do heavy lifting, like hit metadata to understand the method, signature walking, argument checking etc. If the signature is known, a delegate is definitely the right choice.

 

The delegate story has an interesting spin to it. When you create a classic delegate over an instance, it is tied one-to-one to the instance of that type.  If the delegate is over a static function you don’t need to worry about the instance, so the type of the method does not come in to the picture – it’s a simple question of scoping. If the delegate is over an instance, then you have the issue of the instance possibly changing.

 

Note:

It’s worthwhile explaining the last sentence a little further. Consider the following: Class A { void M() } and a delegate void del(). When I do a Delegate.CreateDelegate() over the instance of A.M, and my instance of A changes to another instance of A, I need to recreate the delegate.

 

So consider the scenario where the instance is not always stable, we have introduced the recreation of the delegate into the picture. So now it’s a tradeoff between recreating the delegate and invoking, or using a MethodInfo.Invoke. We can either create the delegate multiple times for each instance, or we can cook up a MethodInfo, and utilize the MethodInfo’s independent binding to the instance (via the API – specifying the receiver object).

 

We have to consider the performance aspects here: creation of the delegate is pretty much the same as the binding of the MethodInfo, and Delegate.Invoke is much faster than MethodInfo.Invoke. The ratio we need to consider to produce a working result, is the ration of creation to invocation. If the ratio is one to one, the MethodInfo.Invoke is probably a better solution. If the ratio of invocation over creation is high, than delegate makes sense. If the ratio is different than these options, the user of the API should go away and think about what make sense (ie: benchmark and find out). There is no real concrete story here, as always, we recommend that the user measure their scenario.

 

2. Known signature and known method name, unknown receiver

 

This scenario is very similar to the previous model, except that the method is identified by name and not via a custom attribute. Besides the startup logic (actually binding to the method), the considerations for the previous case are very similar.

 

An alternative to these cases in a fully trusted application, is the use of the IL instruction “calli”. The obvious drawback of calli is the unverifiable nature of the instruction. Because of this, erroneous usage of calli may lead to type safety and security holes.  However, if used correctly, calli offers a great benefit in both speed and working set performance.

 

One obvious advantage of calli over delegates is that it binds to a signature and can take different types of instances. The usage of delegates requires that the delegate must be recreated when the instance changes. Calli uses a function pointer (IntPtr value) that can be retrieved at startup (Give me the MethodInfo, then the MethodHandle and then call GetFunctionPointer), and is extremely light.

 

Note:

Obviously, you can’t generate a calli call in C#. The way out of this if you’re using C#, you can generate an assembly (Reflection.Emit, ILASM etc) that has a method that takes an IntPtr, and performs a calli. One other thing to note, C++ uses calli extensively for virtuals and invocation.

 

Trusted components (particularly system components) that have tight performance requirements should pay great consideration to this late bound invocation solution.