My question is this:
If I'm going to build a DynamicMethod object, corresponding to a ConstructorInfo.Invoke call, what types of IL do I need to implement in order to cope with all (or most) types of arguments, when I can guarantee that the right type and number of arguments is going to be passed in before I make the call?
Background
I am on my 3rd iteration of my IoC container, and currently doing some profiling to figure out if there are any areas where I can easily shave off large amounts of time being used.
One thing I noticed is that when resolving to a concrete type, ultimately I end up with a constructor being called, using ConstructorInfo.Invoke, passing in an array of arguments that I've worked out.
What I noticed is that the invoke method has quite a bit of overhead, and I'm wondering if most of this is just different implementations of the same checks I do.
For instance, due to the constructor matching code I have, to find a matching constructor for the predefined parameter names, types, and values that I have passed in, there's no way this particular invoke call will not end up with something it should be able to cope with, like the correct number of arguments, in the right order, of the right type, and with appropriate values.
When doing a profiling session containing a million calls to my resolve method, and then replacing it with a DynamicMethod implementation that mimics the Invoke call, the profiling timings was like this:
- ConstructorInfo.Invoke: 1973ms
- DynamicMethod: 93ms
This accounts for around 20% of the total runtime of this profiling application. In other words, by replacing the ConstructorInfo.Invoke call with a DynamicMethod that does the same, I am able to shave off 20% runtime when dealing with basic factory-scoped services (ie. all resolution calls end up with a constructor call).
I think this is fairly substantial, and warrants a closer look at how much work it would be to build a stable DynamicMethod generator for constructors in this context.
So, the dynamic method would take in an object array, and return the constructed object, and I already know the ConstructorInfo object in question.
Therefore, it looks like the dynamic method would be made up of the following IL:
l001: ldarg.0 ; the object array containing the arguments
l002: ldc.i4.0 ; the index of the first argument
l003: ldelem.ref ; get the value of the first argument
l004: castclass T ; cast to the right type of argument (only if not "Object")
(repeat l001-l004 for all parameters, l004 only for non-Object types,
varying l002 constant from 0 and up for each index)
l005: newobj ci ; call the constructor
l006: ret
Is there anything else I need to consider?
Note that I'm aware that creating dynamic methods will probably not be available when running the application in "reduced access mode" (sometimes the brain just won't give up those terms), but in that case I can easily detect that and just calling the original constructor as before, with the overhead and all.