Skip to content
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

Speed up progression searches by using the navigate-to index #54382

Merged
merged 12 commits into from
Jun 25, 2021

Conversation

CyrusNajmabadi
Copy link
Member

The current progression search system works by literally walking the entire solution, producing compilations for all projects and symbols for every symbol therein. This is pretty terrible as it's enormously costly and uses a ton of ram. On my machine it takes a full 2 minutes to search Roslyn.sln and uses at least a GB of ram.

This PR introduces a new implementation of progression search that sits on top of the NavigateTo search index instead. This brings search time down <4 seconds, and uses around 900mb less memory. This also benefits from:

  1. NavigateTo running OutOfProc
  2. all the work we did to dedupe results in navto
  3. the work to allow navto to return results before the solution (or oop) has fully loaded

However, this change is not a pure win. The way progression search works today is that all entries are backed by real symbols. This means that the presented nodes can expose commands (like 'find all callers'). These continue to work for normal progression items. However, this is not supported for tehse new search results (as they're not backed by a real symbol anymore).

The progression team is going to investigate though how often (if ever) these advanced commands are used on search results. We hypothesize that very few people will be affected by this, but we would like data to be sure. So, in the meantime, this will be off by default.

// to.
spanStart = sourceText.Length;
}
var span = NavigateToUtilities.GetBoundedSpan(_searchResult.NavigableItem, sourceText);
Copy link
Member Author

Choose a reason for hiding this comment

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

extracted the above into a helper to use in progression as well.

return result;
}
}
=> NavigateToUtilities.GetKindsProvided(_workspace.CurrentSolution);
Copy link
Member Author

Choose a reason for hiding this comment

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

similar. this moved to a common locatino so Progressin could use it.

Comment on lines 759 to 777
private static string GetIconString(Glyph glyph)
{
return glyph switch
{
Glyph.ClassPublic or Glyph.ClassProtected or Glyph.ClassPrivate or Glyph.ClassInternal => IconHelper.GetIconName("Class", GetAccessibility(glyph, Glyph.ClassPublic)),
Glyph.ConstantPublic or Glyph.ConstantProtected or Glyph.ConstantPrivate or Glyph.ConstantInternal => IconHelper.GetIconName("Field", GetAccessibility(glyph, Glyph.ConstantPublic)),
Glyph.DelegatePublic or Glyph.DelegateProtected or Glyph.DelegatePrivate or Glyph.DelegateInternal => IconHelper.GetIconName("Delegate", GetAccessibility(glyph, Glyph.DelegatePublic)),
Glyph.EnumPublic or Glyph.EnumProtected or Glyph.EnumPrivate or Glyph.EnumInternal => IconHelper.GetIconName("Enum", GetAccessibility(glyph, Glyph.EnumPublic)),
Glyph.EnumMemberPublic or Glyph.EnumMemberProtected or Glyph.EnumMemberPrivate or Glyph.EnumMemberInternal => IconHelper.GetIconName("EnumMember", GetAccessibility(glyph, Glyph.EnumMemberPublic)),
Glyph.ExtensionMethodPublic or Glyph.ExtensionMethodProtected or Glyph.ExtensionMethodPrivate or Glyph.ExtensionMethodInternal => IconHelper.GetIconName("Method", GetAccessibility(glyph, Glyph.ExtensionMethodPublic)),
Glyph.EventPublic or Glyph.EventProtected or Glyph.EventPrivate or Glyph.EventInternal => IconHelper.GetIconName("Event", GetAccessibility(glyph, Glyph.EventPublic)),
Glyph.FieldPublic or Glyph.FieldProtected or Glyph.FieldPrivate or Glyph.FieldInternal => IconHelper.GetIconName("Field", GetAccessibility(glyph, Glyph.FieldPublic)),
Glyph.InterfacePublic or Glyph.InterfaceProtected or Glyph.InterfacePrivate or Glyph.InterfaceInternal => IconHelper.GetIconName("Interface", GetAccessibility(glyph, Glyph.InterfacePublic)),
Glyph.MethodPublic or Glyph.MethodProtected or Glyph.MethodPrivate or Glyph.MethodInternal => IconHelper.GetIconName("Method", GetAccessibility(glyph, Glyph.MethodPublic)),
Glyph.ModulePublic or Glyph.ModuleProtected or Glyph.ModulePrivate or Glyph.ModuleInternal => IconHelper.GetIconName("Module", GetAccessibility(glyph, Glyph.ModulePublic)),
Glyph.PropertyPublic or Glyph.PropertyProtected or Glyph.PropertyPrivate or Glyph.PropertyInternal => IconHelper.GetIconName("Property", GetAccessibility(glyph, Glyph.PropertyPublic)),
Glyph.StructurePublic or Glyph.StructureProtected or Glyph.StructurePrivate or Glyph.StructureInternal => IconHelper.GetIconName("Structure", GetAccessibility(glyph, Glyph.StructurePublic)),
_ => null,
};
Copy link
Member

@Youssef1313 Youssef1313 Jun 25, 2021

Choose a reason for hiding this comment

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

Anyway to instead reuse GetTags?

public static ImmutableArray<string> GetTags(Glyph glyph)

and also:

private static Accessibility GetAccessibility(ImmutableArray<string> tags)

Copy link
Member Author

Choose a reason for hiding this comment

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

yup. THat seems nice. Thanks!

@@ -208,7 +208,7 @@ private static Glyph GetGlyph(string tag, ImmutableArray<string> allTags)
return Glyph.None;
}

private static Accessibility GetAccessibility(ImmutableArray<string> tags)
public static Accessibility GetAccessibility(ImmutableArray<string> tags)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public static Accessibility GetAccessibility(ImmutableArray<string> tags)
public static Accessibility GetAccessibility(this ImmutableArray<string> tags)

very nit here, but I would expect this to be an extension like the others in the file.

Copy link
Member

Choose a reason for hiding this comment

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

nooooooooooooooooooooooooooooooooooooo do not make highly specific extension methods on widely used types like this.

Copy link
Member Author

Choose a reason for hiding this comment

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

i'm not a fan of extending random collections. For example, you could have an ImmutableArray in many different contexts. Having this .GetAccessibility extension show up for all of those seems wonky to me :)

Copy link
Contributor

Choose a reason for hiding this comment

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

almost all static types called XXXExtensions though are extension methods. Should it move instead?

Comment on lines +714 to +716
NavigateToItemKind.Constant or
NavigateToItemKind.EnumItem or
NavigateToItemKind.Field => CodeNodeCategories.Field,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
NavigateToItemKind.Constant or
NavigateToItemKind.EnumItem or
NavigateToItemKind.Field => CodeNodeCategories.Field,
NavigateToItemKind.Constant or
NavigateToItemKind.EnumItem or
NavigateToItemKind.Field => CodeNodeCategories.Field,

This is definitely a style preference, but I find it hard to know that items are linked to a single thing in switch expressions without the indentation.

var groupName = glyph switch
{
Glyph.ClassPublic or Glyph.ClassProtected or Glyph.ClassPrivate or Glyph.ClassInternal => "Class",
Glyph.ConstantPublic or Glyph.ConstantProtected or Glyph.ConstantPrivate or Glyph.ConstantInternal => "Field",
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we use the same glyph for constants and fields?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup! As per GetOrCreateNodeForFieldAsync it looks like we only distinguish fields and enum-members in progression.

@CyrusNajmabadi CyrusNajmabadi merged commit eb02555 into dotnet:main Jun 25, 2021
@ghost ghost added this to the Next milestone Jun 25, 2021
@CyrusNajmabadi CyrusNajmabadi deleted the progression branch June 25, 2021 21:40
@RikkiGibson RikkiGibson modified the milestones: Next, 17.0.P2 Jun 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants