-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Avoid intermediate allocations in MethodInfo/ConstructorInfo.Invoke #50814
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
Conversation
|
Note to reviewers: This will be easier to review with hide whitespace changes set. There's already significant unit and functional test coverage over all the reflection invocation code paths. I can't think offhand of any useful tests I could introduce alongside this change. But please feel free to offer suggestions and I can try to get them in. |
src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs
Show resolved
Hide resolved
| private object? _arg6; | ||
| private object? _arg7; | ||
| #pragma warning restore CA1823, CS0169, IDE0051 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We now have a few of these floating around, e.g.
runtime/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIWrapper.cs
Lines 218 to 224 in 79ae74f
| private ref struct ThreeByteArrays | |
| { | |
| public const int NumItems = 3; | |
| internal byte[] _item0; | |
| private byte[] _item1; | |
| private byte[] _item2; | |
| } |
Lines 97 to 103 in 79ae74f
| private struct FourStackStrings // used to do the equivalent of: Span<string> strings = stackalloc string[4]; | |
| { | |
| public string Item1; | |
| public string Item2; | |
| public string Item3; | |
| public string Item4; | |
| } |
and other places we'd wanted to but just didn't go to the lengths of creating these (there's also the additional zero'ing overhead that comes if you overestimate). At some point, we should think about doing something more general, whether it be enabling runtime support for stackalloc to work with reference types, or language support to create these types for stackallocs with const sizes, or a set of helper types we simply share either in Common or even publicly.
|
Can we add overloads to make these APIs support |
src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs
Outdated
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs
Outdated
Show resolved
Hide resolved
|
@davidfowl Feel free to bring that to API review. Though I suspect you'll be more interested in the other "fast reflection" work being considered, which significantly cuts the remaining overhead from these methods. See other reflection issues assigned to Steve or me for more info. |
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs
Outdated
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
Outdated
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs
Show resolved
Hide resolved
|
Changes in latest update:
My benchmark machine is currently producing lots of noise at the moment (WU, perhaps?). But as a rough estimate it looks about inline with the previous iteration. |
jkotas
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thank you!
Resolves #12832.
The managed reflection stack creates a defensive copy of the parameters
object[]array on calls toMethodInfo.InvokeandConstructorInfo.Invoketo prevent the parameter values changing between the initial type safety check and the real invocation of the target method. This PR special-cases "small" arrays (currently defined as <= 8 arguments) and creates a stack-based defensive copy instead of a heap-based defensive copy. This saves up to 88 bytes of heap allocations (on x64) perMethodInfo.InvokeorConstructorInfo.Invokecall.If 9 or more parameters are present, we fall back to the normal "allocate an array on the heap" code path.
I also took this opportunity to refactor some code that was on the
Invokehot paths, such as lazy initialization of theInvocationFlagsandSignatureproperties. These help offset some of the cost of setting up the struct-based argument holder.