-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[API Proposal]: Add invoker classes for fast invoke APIs #85539
Comments
Tagging subscribers to this area: @dotnet/area-system-reflection Issue DetailsBackground and motivation(placeholder) API Proposal(placeholder) API Usage(placeholder) Alternative Designs(placeholder) Risks(placeholder)
|
public class MethodInvoker
{
public ConstructorInvoker Create(MethodBase info);
...
}
public class ConstructorInvoker
{
public ConstructorInvoker Create(ConstructorInfo info);
...
// We do not need this ptr for these.
public object? Invoke(Span<object?> arguments);
public object? Invoke(object? arg1);
public object? Invoke(object? arg1, object? arg2);
public object? Invoke(object? arg1, object? arg2, object? arg3);
public object? Invoke(object? arg1, object? arg2, object? arg3, object? arg4);
} |
How about adding extension methods to |
The extension methods would have to first check for null "this" and then cast the MethodInfo to RuntimeMethodInfo. It is unnecessary overhead. The only reason why we are adding this is to squeeze performance out of reflection invoke. |
Unless I'm unaware of a recent language change, extension methods can only exist in static classes, so this won't work. |
Guess it's a typo, an extension method for |
namespace System.Reflection
{
public sealed class ConstructorInvoker
{
public static ConstructorInvoker Create(ConstructorInfo constructor);
private ConstructorInvoker();
public object? Invoke();
public object? Invoke(object? arg1);
public object? Invoke(object? arg1, object? arg2);
public object? Invoke(object? arg1, object? arg2, object? arg3);
public object? Invoke(object? arg1, object? arg2, object? arg3, object? arg4);
public object? Invoke(Span<object?> arguments);
}
public sealed class MethodInvoker
{
public static MethodInvoker Create(MethodInfo method);
private MethodInvoker();
public object? Invoke(object? obj);
public object? Invoke(object? obj, object? arg1);
public object? Invoke(object? obj, object? arg1, object? arg2);
public object? Invoke(object? obj, object? arg1, object? arg2, object? arg3);
public object? Invoke(object? obj, object? arg1, object? arg2, object? arg3, object? arg4);
public object? Invoke(object? obj, Span<object?> arguments);
}
} |
Nit: We may want to make this |
We should try replacing our method info invocation with this and test the performance. |
Why not make the Invoke methods generic so that people can avoid boxing? public TResult Invoke<T1, TResult>(T1 arg1);
public TResult Invoke<T1, T2, TResult>(T1 arg1, T2 arg2);
public TResult Invoke<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);
public TResult Invoke<T1, T2, T3, T4, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
// and i would add more... until 8 or 16 so that people can avoid having to use the boxing Span overload |
Actually, I think it would be a lot better and a lot more user friendly to make the class itself generic, so that when it's instantiated, it already has the right Invoke method with the right type parameters and the user can't accidentally use different ones. That would require a lot more invoker classes, though, e.g.: public sealed class MethodInvoker<TObj, TResult>
{
public TResult Invoke(TObj obj);
}
public sealed class MethodInvoker<TObj, TArg1, TResult>
{
public TResult Invoke(TObj obj, TArg1 arg);
}
public sealed class MethodInvoker<TObj, TArg1, TArg2, TResult>
{
public TResult Invoke(TObj obj, TArg1 arg, TArg2);
}
// ...etc It would be a lot more user friendly though and more importantly, avoid any boxing. Or I'm thinking there might be a way to still only have one class by having the type parameter be a delegate: public static class MethodInvoker
{
public static MethodInvoker<TDelegate> Create<TDelegate>(MethodInfo method) where TDelegate : Delegate;
}
public sealed class MethodInvoker<TDelegate> where TDelegate : Delegate
{
public TDelegate Invoke { get; }
} Usage would look like this: var invoker = MethodInvoker.Create<Func<int, string>>(myMethod);
string result = invoker.Invoke(5); although this second approach might add a bit of overhead by having to allocate the delegate. But that would only be a one-time cost when creating the invoker and invoking the method would still avoid any boxing. And the implementation could actually be even more efficient because it could use reflection emit to prepare the invoke delegate since it already has the right parameter count and parameter types. If that were the case, this would be the most efficient way to dynamically invoke a method as it would have the same perf as manually using reflection emit. People who use reflection emit to invoke a method today could delete all that code and instead use this because it would have the same exact perf. I see no point in the API as currently proposed because it still boxes and it still wouldn't be the fastest way to invoke a method. The approach I propose would be a) more user friendly by being strongly typed and b) faster as well - as fast as it can get. |
Most often when reflection is used, it's to deal with types you don't know, so you can't provide an appropriate type argument. |
Interesting, I guess I use reflection differently. IIRC, any time I've used reflection invoke APIs, I always had strongly typed values and having to use an object array was just an ugly necessary evil. At least we could have both options. the EDIT: Never mind. I had no idea I could just use |
Oh wow, it seems I can finally retire my method invokers in .NET8+. I can't wait for trying it out. |
Note that |
@steveharter are you planning to update DI to use the |
Background and motivation
This will add "fast invoke" capability, allowing for up to 2x CPU improvement and eliminating the
object[] parameters
alloc for some scenarios. The existingMethodBase.Invoke(...)
will also leverage this when possible. It is part of a larger goal to reduce usage of Reflection.Emit where feasible, which means performance is critical since Emit was originally used over Invoke because of performance.API Proposal
New types:
API Usage
Simple replacement for MethodBase.Invoke:
For high-performance APIs such as DI's ActivatorUtiliities.CreateFactory():
Validation
Alternative Designs
Note that these proposed invoker classes already exist internally.
Support for byref-like types is covered here which has been prototyped successfully. However, the performance of that is not ideal when only
System.Object
is necessary which is the case for DI's CreateFactory which calls constructors with user-specifiedobject
- based args.The text was updated successfully, but these errors were encountered: