-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Improve performance of interface method resolution in ILC #103066
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -89,7 +89,7 @@ public static bool MightHaveInterfaceDispatchMap(TypeDesc type, NodeFactory fact | |
|
||
DefType declType = type.GetClosestDefType(); | ||
|
||
for (int interfaceIndex = 0; interfaceIndex < declType.RuntimeInterfaces.Length; interfaceIndex++) | ||
for (int interfaceIndex = declType.RuntimeInterfaces.Length - 1; interfaceIndex >= 0; interfaceIndex--) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this method is only returning a Boolean I think this is equivalent, but could you ensure that there’s no possible condition in which this could change the result? I think what I want to know is the invariants that need to hold for the same behavior to occur. Something like: the search returns true if any of the items are true, and checks on earlier items don’t affect the checks on later items (each check is independent) so this effectively forms a Contains call that is order invariant. But I don’t if that’s true (the code/type system is complex). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is quite trivial to reason about. There's no |
||
{ | ||
DefType interfaceType = declType.RuntimeInterfaces[interfaceIndex]; | ||
InstantiatedType interfaceOnDefinitionType = interfaceType.IsTypeDefinition ? | ||
|
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.
When can this be null now?
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.
There's still one branch that can return
null
in theory:runtime/src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs
Line 659 in 9b20327
I don't think it can happen in practice since it's likely impossible code path in the grand scheme of things but I didn't feel safe enough to reason about it.
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.
And if it does we go back through the loop with the base type… but didn’t we already do that in the first call? Is there a way this could return anything except null?
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.
That's precisely what this PR fixes. If we call
ResolveInterfaceMethodToVirtualMethodOnType
recursively from insideResolveInterfaceMethodToVirtualMethodOnTypeRecursive
we just return and use the result instead of throwing it away (ie. returningnull
) and then going to the base type (next step of the outer loop) and computing the exact same thing again.--
There are three cases when
ResolveInterfaceMethodToVirtualMethodOnType
returnsnull
:if (currentType.IsInterface)
if (!IsInterfaceImplementedOnType(currentType, interfaceType))
MethodDesc baseClassImplementationOfInterfaceMethod = ResolveInterfaceMethodToVirtualMethodOnTypeRecursive(interfaceMethod, baseType);
Presumably, 1. can only happen once and will never walk the type hierarchy through the base type. I didn't study the case 2. in detail but it should not matter, worst case it ends up doing the same thing it did now. Case 3. is the one I am trying to optimize.
If we are already in the
ResolveInterfaceMethodToVirtualMethodOnTypeRecursive
loop then returningnull
at 3. would continue the loop and proceed to base type. That's exactly the same thing we just computed by callingResolveInterfaceMethodToVirtualMethodOnTypeRecursive(interfaceMethod, baseType);
though, so instead of returningnull
in the case (and specifically only in this case, hence the added parameter) just return the computed value and short-circuit the outer loop.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.
Or, put another way, the
returnRecursive: true
indicates that ifx = ResolveInterfaceMethodToVirtualMethodOnTypeRecursive(interfaceMethod, baseType);
is computed ANDx is not null
then it changes the behavior toreturn x;
instead ofreturn null
.Previously it returned
null
, the loop proceeded to setcurrentType = currentType.MetadataBaseType;
and continuing the loop would be equivalent to executing the recursive formreturn ResolveInterfaceMethodToVirtualMethodOnTypeRecursive(interfaceMethod, currentType.MetadataBaseType);
.The observation is that we just computed it and threw it away... so let's not throw it away.