Skip to content

Improve searching for symbols when a name is known ahead of time. (Including Compilation.GetEntryPoint())#26325

Merged
jcouv merged 23 commits intodotnet:masterfrom
CyrusNajmabadi:symbolSearchByName
Apr 24, 2018
Merged

Improve searching for symbols when a name is known ahead of time. (Including Compilation.GetEntryPoint())#26325
jcouv merged 23 commits intodotnet:masterfrom
CyrusNajmabadi:symbolSearchByName

Conversation

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi commented Apr 22, 2018

Found as part of this review: #26316

@jcouv Would like to call compilation.GetEntryPoint inside an analyzer. However, this is currently very expensive as it will realize all namespace, type and member symbols in a compilation. We can improve things greatly here through use of the decl table. We only need to analyze types that actually contain a 'Main' member in them, vastly decreasing the amount of work and garbage that is necessary.

I've also decreased the cost of decl table searches. Avoiding linq, and other unnecessary allocations when we hit each decl.

@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner April 22, 2018 19:08
@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

CyrusNajmabadi commented Apr 22, 2018

Tagging @jcouv @dotnet/roslyn-compiler . #Resolved

@jcouv jcouv added Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee. labels Apr 22, 2018
@jcouv jcouv added this to the 15.8 milestone Apr 22, 2018
/// <summary>
/// Return source declaration symbols whose name matches the provided name
/// </summary>
internal abstract IEnumerable<ISymbol> GetSymbolsWithName(string name, SymbolFilter filter = SymbolFilter.TypeAndMember, CancellationToken cancellationToken = default(CancellationToken));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i woudl like to make this public. This would also be a valuable member for the FindReferencesEngine to have. Currently we have to call into the member above this which is quite expensive.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will do that in another PR though.

@CyrusNajmabadi CyrusNajmabadi changed the title Improve searching for symbols when a name is known ahead of time. Improve searching for symbols when a name is known ahead of time. (Including Compilation.GetEntryPoint()) Apr 22, 2018
}

private class SymbolSearcher
private abstract class AbstractSymbolSearcher
Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: i would like to move these to sibling files. However, i didn't want to interfere with teh diff, so i kept them in this file for now. I'll move if that's what people would like, once this is reviewed. #Resolved

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend doing the move in a separate iteration of this PR.


In reply to: 183247833 [](ancestors = 183247833)

Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. #Resolved

{
continue;
}
}
Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this moved into MethodSymbol.IsEntryPointCandidate #Resolved

Dim method = DirectCast(member, MethodSymbol)
' check if this is an entry point
If Not method.IsSubmissionConstructor AndAlso _entryPointCandidates IsNot Nothing AndAlso Not method.IsImplicitlyDeclared AndAlso method.IsEntryPointCandidate Then
If _entryPointCandidates IsNot Nothing AndAlso method.IsEntryPointCandidate Then
Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the other checks moved into IsEntryPointCandidate. #Resolved

Return True
End If
Next
Next
Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: it's a pity we can't just do typeDecl.MemberNames.Contains(_name) However, MemberNames is just an array, and not a set with an appropriate comparer.

This means hte algorithm is O(n) with the number of member names to check. If we can actually use a set here, then we are only O(n) with the number of types to check. There are vastly less types than members, so that would be a substantive improvement.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could potentially replace that array with a SmallDictionary.Keys, but that could potentially have larger remifications. Personally, i don't htink it would be an issue, as this would be just one small change per TypeDeclaration, but it seemed out of scope of this PR. @VSadov for his thoughts here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: i was the original person who added MemberNames to the decl table. It was always intended, from the beginning, to be a fast way to check for existence of a member and/or to get that member quickly. I believe i started things out with a set, but it's so lost in the annals of time for me that i'm not sure. However, it does feel like this would be an easy tweak to make, with minimal issues, for the beneficial goal of speeding up these queries. Being able to just do simple O(1) lookup per type, instead of N checks would be very nice.

@gafter
Copy link
Copy Markdown
Member

gafter commented Apr 23, 2018

Very nice. LGTM #Resolved

Copy link
Copy Markdown
Member

@gafter gafter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

CyrusNajmabadi commented Apr 23, 2018

Is this used anywhere? If not please delete it.

@gafter Unfortunately yes. It's used when the user supplies a class name, and is what finds the right method inside that class. That said, that's soooo narrow at this point, that it could be removed with the client just enumerating the methods of the class for an appropriate candidate. If you'd like, i can do that. Let me know. #Resolved

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

CyrusNajmabadi commented Apr 23, 2018

@jcouv Can you take a look? #Resolved

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

CyrusNajmabadi commented Apr 23, 2018

@gafter i removed the types as they served practically no purpose anymore. #Resolved

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

CyrusNajmabadi commented Apr 24, 2018

@jcouv Thoughts? #Resolved

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Apr 24, 2018

Looking now #Resolved


protected override bool ShouldCheckTypeForMembers(MergedTypeDeclaration current)
{
foreach (var typeDecl in current.Declarations)
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you type this var. Thanks #Closed

Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! #resolved #Resolved

}
get
{
if (this.IsPartialDefinition() &&
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this already covered by test? #Closed

Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes: ERR_NoEntryPoint_PartialClass (and siblings). #resolved #Resolved

Assert.Throws<ArgumentException>(() =>
{
var compilation = GetTestCompilation();
compilation.GetSymbolsWithName("", SymbolFilter.None);
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetSymbolsWithName [](start = 28, length = 18)

I expect a similar case for ContainsSymbolsWithName (passing in a name, but SymbolFilter.None) #Closed

Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do. #Closed

Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. #resolved #Closed

private static void Test(CSharpCompilation compilation, string name, bool includeNamespace, bool includeType, bool includeMember, int count)
{
var filter = SymbolFilter.None;
filter = includeNamespace ? filter | SymbolFilter.Namespace : filter;
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider parens for readability #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. #resolved.

End Function

''' <summary>
''' Return true if there Is a source declaration symbol name that matches the provided name.
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is [](start = 33, length = 2)

Typo: Is from copy&paste (in a couple of places) #Closed

Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#fixed. thanks! #Resolved

''' <returns>True if the method can be used as an entry point.</returns>
Friend ReadOnly Property IsEntryPointCandidate As Boolean
Get
If Me.ContainingType.IsEmbedded Then
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were those bugs? Do we have tests to cover? #Closed

Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was just code that was in the EntryPointWalker. I just moved it into this method so that the logic for entrypoitn consideration was localized into one single location. #Closed


Assert.Throws(Of ArgumentException)(Sub()
Dim compilation = GetTestCompilation()
compilation.GetSymbolsWithName("", SymbolFilter.None)
Copy link
Copy Markdown
Member

@jcouv jcouv Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here (test ContainsSymbolsWithName with None) #Closed

Copy link
Copy Markdown
Contributor Author

@CyrusNajmabadi CyrusNajmabadi Apr 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure #Closed

Copy link
Copy Markdown
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with review pass (iteration 22).
Feels like some tests may be missing relative to change in entry point detection logic.

Copy link
Copy Markdown
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Thanks!

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Apr 24, 2018

@jaredpar for approval to merge for 15.8. Thanks

@jcouv jcouv self-assigned this Apr 24, 2018
@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

retest ubuntu_16_debug_prtest please

@jaredpar
Copy link
Copy Markdown
Member

approved

@jcouv jcouv merged commit 93b8412 into dotnet:master Apr 24, 2018
@CyrusNajmabadi CyrusNajmabadi deleted the symbolSearchByName branch April 24, 2018 22:10
gafter pushed a commit that referenced this pull request May 31, 2018
…names searches. (#26331)

Followup to #26325 and #26330. This PR updates the IDE to forward certain helpers to these more efficient implementations.

This helps things out by more quickly being able to determine if a type even contains a member with name, and thus whether or not it should even be hydrated into a symbol and have its members created. Previous we would have to do a linear scan on all the members in a type to determine this. Now this data is in a set which can be queried much more efficiently.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Approved to merge Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants