Introduction

Reflection is a feature that allows a program to find out type (and metadata) information about objects at run-time. Programs written in languages that support Reflection, like Java and the CLR languages family (C#, Visual Basic .NET etc.) can inspect types, obtain detailed information about class members, dynamically instantiate classes and invoke methods at run-time. The .NET Framework exposes its reflection services through the System.Reflection namespace. Late-bound invocation, for instance, can be achieved through the Type.InvokeMember and MemberInfo.Invoke methods. The use of Reflection, however, comes with a price. While some of its functions are pretty fast, some others, like late-bound invocation routines, are costly and if not used wisely could result in a major bottleneck inside your application.

The basic idea presented by Joel Polbar is to obtain a method handle through the common Reflection services, and then emit MSIL code to patch up a static call site for the method. This call site will be kept as simple as possible: no type safety / security checks will be performed, assuming that hybrid invocation is done only between fully trusted code entities. Generation of the MSIL call site will be done through a new feature of the .NET Framework 2.0, called Lightweight Code Generation (LGC). Hybrid invocation can be achieved also under .NET Framework 1.1 using the standard Reflection.Emit facilities, but at the price of a heavier implementation.

The DynamicMethod Class

Lightweight Code Generation features are exposed by a single new class inside the System.Reflection.Emit namespace: DynamicMethod. You can use this class to generate and execute a method at run time, without having to generate a dynamic assembly and a dynamic type to contain the method. Dynamic methods are the most efficient way to generate and execute small amounts of code.

The delegate syntax is the same as one of the MethodInfo.Invoke overloads: it takes an object instance (that can be null in the case of static methods) and an array of arguments, and returns a generic object instance.

In this section we compare the length of the provided input arguments array with the number of parameters accepted by the method. On equality, we proceed to the next section (marked with the argsOK label), otherwise a TargetParameterCountException is thrown.

Object instance push

Clear and simple. The function call MSIL opcodes (call, calli, callvirt) need the target object reference to be pushed onto the stack before the arguments. This obviously applies only in the case of non-static method calls.

We read each element inside the args array and push it onto the stack. Unboxing is performed in the case of value type parameters (since the args array is a collection of references, we have to convert a reference to a value to the value itself).

Here we generate the actual method call, process a possible return value, and return from the call site. If our method is final (not virtual nor an interface implementation method) we emit a light call, otherwise a callvirt is required. If a method return value exists and is a value type, it needs to be boxed, because our DynamicMethod delegate returns a generic object reference. If the method has a void return type, we simply make our delegate return a null value, pushing it onto the stack.

Performance Comparisons

As you can see, the DynamicMethod delegate implementation has been kept as simple as possible. The major overhead over a standard early-bound call consists in the need to box the return value when it is a value type. This is a costly operation, because it implies a heap allocation and a copy of the value being boxed. Boxing overhead can occur even during the creation of the input arguments array, but this has not been taken into account in performance measurements, because it is part of the user code.

I measured the efficiency of DynamicMethod delegates vs. standard late-bound invocation in three different calling scenarios with the following results (Note: 1 is the efficiency of a standard direct call in the same calling scenario):

DynamicMethod delegates' performance proves to be superior (in terms of time efficiency) in all the proposed calling scenarios. When return value boxing occurs, however, both late-bound invocation and hybrid invocation suffer a heavy efficiency loss, as expected.

Conclusion

I've tried to illustrate a simple implementation of one of the ideas exposed inside Joel Polbar's article, along with some observations over the implementation itself, hoping that this work will be at least partly useful in understanding what hybrid invocation is and how it can be achieved. As a conclusive note, I'd like to underline that this technique is not a substitute of standard late-bound invocation through Reflection. DynamicMethod delegates should be used instead of MethodInfo.Invoke or analog solutions only when a method (or property accessor) that isn't known at design time needs to be called with medium-high frequency inside a fully trusted security scenario.

The Unbox_any can be replaced by:
il.Emit(OpCodes.Unbox, parmType);
il.Emit(OpCodes.Ldobj, parmType);

In .NET 1.1 the dynamic method must be generated to a dynamic type in a dynamic assembly, for there is no DynamicMethod class.

A much faster way to call such a dynamic method is through an interface, say IFastInvoker, that is implemented by every dynamic type, that is created for every distinct method signature.

My benchmarks show (1`000`000 calls), that under .NET 1.1,
* aMethodInfo.Invoke(..) is as slow as aDelegate.DynamicInvoke(..),
* which is approx. 20x slower than the IFastInvoker call to the dynamic method,
* which in turn is approx. 4x slower than simple interface call.
I couldn`t measure the speed of direct calls, they were too fast .

I also modified some of the IL asm code to enable nulls to be passed instead of empty arrays.