-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Improve performance of IsCollectible reflection properties #119743
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
These properties are often used for non-collectible fast paths. It is important that they are cheap. Converted these proproperties to FCalls to reduce overhead. Fixes dotnet#119499
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.
Pull Request Overview
This PR improves the performance of IsCollectible reflection properties by converting them from QCalls to FCalls to reduce overhead. The change affects RuntimeType, RuntimeMethodInfo, and RuntimeAssembly IsCollectible properties, which are frequently used in non-collectible fast paths where performance is critical.
Key changes:
- Converted QCall implementations to FCall implementations for better performance
- Added new IsCollectible methods to TypeHandle and MethodDesc classes
- Updated method signatures and calling conventions across the reflection API
Reviewed Changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/typehandle.h | Added IsCollectible method declaration to TypeHandle class |
| src/coreclr/vm/typehandle.cpp | Implemented IsCollectible method and updated GetLoaderAllocator contract |
| src/coreclr/vm/runtimehandles.h | Added FCall declarations for IsCollectible methods and removed QCall declarations |
| src/coreclr/vm/runtimehandles.cpp | Converted IsCollectible implementations from QCall to FCall pattern |
| src/coreclr/vm/qcallentrypoints.cpp | Removed QCall entry points for IsCollectible methods |
| src/coreclr/vm/method.hpp | Added IsCollectible method declaration to MethodDesc class |
| src/coreclr/vm/method.cpp | Implemented MethodDesc::IsCollectible and updated GetLoaderAllocator contract |
| src/coreclr/vm/ecalllist.h | Added FCall entries for IsCollectible methods |
| src/coreclr/vm/assemblynative.hpp | Added FCall declaration and removed QCall declaration for Assembly IsCollectible |
| src/coreclr/vm/assemblynative.cpp | Converted Assembly IsCollectible from QCall to FCall implementation |
| src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs | Simplified IsCollectible property to use new FCall |
| src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs | Updated method signatures from QCall to FCall pattern |
| src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs | Updated IsCollectible implementation with GC.KeepAlive for safety |
| src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs | Updated IsCollectible implementation with GC.KeepAlive for safety |
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs
Show resolved
Hide resolved
|
@EgorBot -intel using BenchmarkDotNet.Attributes;
public class Bench
{
[Benchmark]
public bool IsCollectible() => typeof(object).IsCollectible;
} |
|
@EgorBot -intel using BenchmarkDotNet.Attributes;
public class Bench
{
[Benchmark]
public bool IsCollectible() => typeof(object).IsCollectible;
} |
d21576c to
17fbeb1
Compare
17fbeb1 to
d0b10de
Compare
|
I ran the benchmark I was using to investigate this issue against this branch. Good is before regression, bad is after, cache is my PR which cached IsCollectible, and fcall is this PR.
LGTM |
|
thanks for the optimization! |
It's curious that the combination of these changes resulted in a net speed-up, but I do consistently measure that. Benchmark code is: [Benchmark]
[Arguments(typeof(int))] // primitive
// [Arguments(typeof(int?))] // nullable primitive
// [Arguments(typeof(Enum))] // enum
// [Arguments(typeof(Guid))] // built-in value type
// [Arguments(typeof(string))] // built-in reference type
public TypeConverter GetConverter(Type typeToConvert) => TypeDescriptor.GetConverter(typeToConvert);All changes were based off a starting commit of b6127f9. That was the "bad" data point - using a local build. "good" was after reverting 6a8720f, same process as bad. Cache and fcall were based on the same starting commit, applying mine and @jkotas's changes respectively. I would have expected us to get close to |
|
These properties are often used for non-collectible fast paths. It is important that they are cheap. Converted these properties to FCalls to reduce overhead.
Fixes #119499