Skip to content

Commit

Permalink
Merge pull request #14140 from CyrusNajmabadi/indexAllocations
Browse files Browse the repository at this point in the history
Reduce the number of intermediary IEnumerables we create while buildind indices
  • Loading branch information
CyrusNajmabadi committed Sep 28, 2016
2 parents c3fa51d + 548f47a commit 94ef59b
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 193 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
Expand Down Expand Up @@ -34,7 +35,7 @@ protected SearchScope(AbstractAddImportCodeFixProvider<TSimpleNameSyntax> provid
CancellationToken = cancellationToken;
}

protected abstract Task<IEnumerable<ISymbol>> FindDeclarationsAsync(string name, SymbolFilter filter, SearchQuery query);
protected abstract Task<ImmutableArray<ISymbol>> FindDeclarationsAsync(string name, SymbolFilter filter, SearchQuery query);
public abstract SymbolReference CreateReference<T>(SymbolResult<T> symbol) where T : INamespaceOrTypeSymbol;

public async Task<IEnumerable<SymbolResult<ISymbol>>> FindDeclarationsAsync(
Expand Down Expand Up @@ -109,7 +110,7 @@ public AllSymbolsProjectSearchScope(
{
}

protected override Task<IEnumerable<ISymbol>> FindDeclarationsAsync(
protected override Task<ImmutableArray<ISymbol>> FindDeclarationsAsync(
string name, SymbolFilter filter, SearchQuery searchQuery)
{
return SymbolFinder.FindDeclarationsAsync(_project, searchQuery, filter, CancellationToken);
Expand All @@ -133,14 +134,15 @@ public SourceSymbolsProjectSearchScope(
_projectToAssembly = projectToAssembly;
}

protected override async Task<IEnumerable<ISymbol>> FindDeclarationsAsync(string name, SymbolFilter filter, SearchQuery searchQuery)
protected override async Task<ImmutableArray<ISymbol>> FindDeclarationsAsync(
string name, SymbolFilter filter, SearchQuery searchQuery)
{
var service = _project.Solution.Workspace.Services.GetService<ISymbolTreeInfoCacheService>();
var info = await service.TryGetSourceSymbolTreeInfoAsync(_project, CancellationToken).ConfigureAwait(false);
if (info == null)
{
// Looks like there was nothing in the cache. Return no results for now.
return SpecializedCollections.EmptyEnumerable<ISymbol>();
return ImmutableArray<ISymbol>.Empty;
}

// Don't create the assembly until it is actually needed by the SymbolTreeInfo.FindAsync
Expand Down Expand Up @@ -190,14 +192,14 @@ public override SymbolReference CreateReference<T>(SymbolResult<T> searchResult)
_metadataReference);
}

protected override async Task<IEnumerable<ISymbol>> FindDeclarationsAsync(
protected override async Task<ImmutableArray<ISymbol>> FindDeclarationsAsync(
string name, SymbolFilter filter, SearchQuery searchQuery)
{
var service = _solution.Workspace.Services.GetService<ISymbolTreeInfoCacheService>();
var info = await service.TryGetMetadataSymbolTreeInfoAsync(_solution, _metadataReference, CancellationToken).ConfigureAwait(false);
if (info == null)
{
return SpecializedCollections.EmptyEnumerable<ISymbol>();
return ImmutableArray<ISymbol>.Empty;
}

return await info.FindAsync(searchQuery, _assembly, filter, CancellationToken).ConfigureAwait(false);
Expand Down
138 changes: 89 additions & 49 deletions src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_Declarations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
Expand Down Expand Up @@ -117,7 +118,7 @@ public static partial class SymbolFinder
/// <summary>
/// Find the declared symbols from either source, referenced projects or metadata assemblies with the specified name.
/// </summary>
public static Task<IEnumerable<ISymbol>> FindDeclarationsAsync(
public static async Task<IEnumerable<ISymbol>> FindDeclarationsAsync(
Project project, string name, bool ignoreCase, CancellationToken cancellationToken = default(CancellationToken))
{
if (name == null)
Expand All @@ -127,16 +128,17 @@ public static Task<IEnumerable<ISymbol>> FindDeclarationsAsync(

if (string.IsNullOrWhiteSpace(name))
{
return SpecializedTasks.EmptyEnumerable<ISymbol>();
return SpecializedCollections.EmptyEnumerable<ISymbol>();
}

return FindDeclarationsAsync(project, SearchQuery.Create(name, ignoreCase), SymbolFilter.All, cancellationToken: cancellationToken);
return await FindDeclarationsAsync(
project, SearchQuery.Create(name, ignoreCase), SymbolFilter.All, cancellationToken: cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Find the declared symbols from either source, referenced projects or metadata assemblies with the specified name.
/// </summary>
public static Task<IEnumerable<ISymbol>> FindDeclarationsAsync(
public static async Task<IEnumerable<ISymbol>> FindDeclarationsAsync(
Project project, string name, bool ignoreCase, SymbolFilter filter, CancellationToken cancellationToken = default(CancellationToken))
{
if (name == null)
Expand All @@ -146,13 +148,14 @@ public static Task<IEnumerable<ISymbol>> FindDeclarationsAsync(

if (string.IsNullOrWhiteSpace(name))
{
return SpecializedTasks.EmptyEnumerable<ISymbol>();
return SpecializedCollections.EmptyEnumerable<ISymbol>();
}

return FindDeclarationsAsync(project, SearchQuery.Create(name, ignoreCase), filter, cancellationToken: cancellationToken);
return await FindDeclarationsAsync(
project, SearchQuery.Create(name, ignoreCase), filter, cancellationToken: cancellationToken).ConfigureAwait(false);
}

internal static Task<IEnumerable<ISymbol>> FindDeclarationsAsync(
internal static Task<ImmutableArray<ISymbol>> FindDeclarationsAsync(
Project project, SearchQuery query, SymbolFilter filter, CancellationToken cancellationToken)
{
// All entrypoints to this function are Find functions that are only searching
Expand All @@ -166,7 +169,7 @@ internal static Task<IEnumerable<ISymbol>> FindDeclarationsAsync(

if (query.Name != null && string.IsNullOrWhiteSpace(query.Name))
{
return SpecializedTasks.EmptyEnumerable<ISymbol>();
return SpecializedTasks.EmptyImmutableArray<ISymbol>();
}

using (Logger.LogBlock(FunctionId.SymbolFinder_FindDeclarationsAsync, cancellationToken))
Expand All @@ -175,7 +178,7 @@ internal static Task<IEnumerable<ISymbol>> FindDeclarationsAsync(
}
}

private static async Task<IEnumerable<ISymbol>> FindDeclarationsAsyncImpl(
private static async Task<ImmutableArray<ISymbol>> FindDeclarationsAsyncImpl(
Project project, SearchQuery query, SymbolFilter criteria, CancellationToken cancellationToken)
{
// All entrypoints to this function are Find functions that are only searching
Expand All @@ -184,7 +187,7 @@ private static async Task<IEnumerable<ISymbol>> FindDeclarationsAsyncImpl(

var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

var list = new List<ISymbol>();
var list = ArrayBuilder<ISymbol>.GetInstance();

// get declarations from the compilation's assembly
await AddDeclarationsAsync(project, query, criteria, list, cancellationToken).ConfigureAwait(false);
Expand All @@ -205,7 +208,7 @@ await AddDeclarationsAsync(
}
}

return TranslateNamespaces(list, compilation);
return TranslateNamespaces(list.ToImmutableAndFree(), compilation);
}

private static string GetMetadataReferenceFilePath(MetadataReference metadataReference)
Expand All @@ -216,67 +219,92 @@ private static string GetMetadataReferenceFilePath(MetadataReference metadataRef
/// <summary>
/// Makes certain all namespace symbols returned by API are from the compilation.
/// </summary>
private static IEnumerable<ISymbol> TranslateNamespaces(List<ISymbol> symbols, Compilation compilation)
private static ImmutableArray<ISymbol> TranslateNamespaces(
ImmutableArray<ISymbol> symbols, Compilation compilation)
{
var builder = ArrayBuilder<ISymbol>.GetInstance();
foreach (var symbol in symbols)
{
var ns = symbol as INamespaceSymbol;
if (ns != null)
{
yield return compilation.GetCompilationNamespace(ns);
builder.Add(compilation.GetCompilationNamespace(ns));
}
else
{
yield return symbol;
builder.Add(symbol);
}
}

var result = builder.Count == symbols.Length
? symbols
: builder.ToImmutable();

builder.Free();

return result;
}

private static async Task AddDeclarationsAsync(
Project project, SearchQuery query, SymbolFilter filter, List<ISymbol> list, CancellationToken cancellationToken)
private static Task AddDeclarationsAsync(
Project project, SearchQuery query, SymbolFilter filter,
ArrayBuilder<ISymbol> list, CancellationToken cancellationToken)
{
await AddDeclarationsAsync(
return AddDeclarationsAsync(
project, query, filter, list,
startingCompilation: null,
startingAssembly: null,
cancellationToken: cancellationToken).ConfigureAwait(false);
cancellationToken: cancellationToken);
}

private static async Task AddDeclarationsAsync(
Project project,
SearchQuery query,
SymbolFilter filter,
List<ISymbol> list,
ArrayBuilder<ISymbol> list,
Compilation startingCompilation,
IAssemblySymbol startingAssembly,
CancellationToken cancellationToken)
{
using (Logger.LogBlock(FunctionId.SymbolFinder_Project_AddDeclarationsAsync, cancellationToken))
using (var set = SharedPools.Default<HashSet<ISymbol>>().GetPooledObject())
{
if (!await project.ContainsSymbolsWithNameAsync(query.GetPredicate(), filter, cancellationToken).ConfigureAwait(false))
{
return;
}

var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
if (startingCompilation != null && startingAssembly != null && compilation.Assembly != startingAssembly)
{
// Return symbols from skeleton assembly in this case so that symbols have the same language as startingCompilation.
list.AddRange(
FilterByCriteria(compilation.GetSymbolsWithName(query.GetPredicate(), filter, cancellationToken), filter)
.Select(s => s.GetSymbolKey().Resolve(startingCompilation, cancellationToken: cancellationToken).Symbol).WhereNotNull());
}
else
{
list.AddRange(FilterByCriteria(compilation.GetSymbolsWithName(query.GetPredicate(), filter, cancellationToken), filter));
}
var unfilteredSymbols = await GetUnfilteredSymbolsAsync(
project, query, filter, startingCompilation, startingAssembly, cancellationToken).ConfigureAwait(false);
list.AddRange(FilterByCriteria(unfilteredSymbols, filter));
}
}

private static async Task<ImmutableArray<ISymbol>> GetUnfilteredSymbolsAsync(
Project project,
SearchQuery query,
SymbolFilter filter,
Compilation startingCompilation,
IAssemblySymbol startingAssembly,
CancellationToken cancellationToken)
{
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
if (startingCompilation != null && startingAssembly != null && compilation.Assembly != startingAssembly)
{
// Return symbols from skeleton assembly in this case so that symbols have the same language as startingCompilation.
return compilation.GetSymbolsWithName(query.GetPredicate(), filter, cancellationToken)
.Select(s => s.GetSymbolKey().Resolve(startingCompilation, cancellationToken: cancellationToken).Symbol)
.WhereNotNull()
.ToImmutableArray();
}
else
{
return compilation.GetSymbolsWithName(query.GetPredicate(), filter, cancellationToken)
.ToImmutableArray();
}
}

private static async Task AddDeclarationsAsync(
Solution solution, IAssemblySymbol assembly, PortableExecutableReference referenceOpt,
SearchQuery query, SymbolFilter filter, List<ISymbol> list, CancellationToken cancellationToken)
SearchQuery query, SymbolFilter filter, ArrayBuilder<ISymbol> list, CancellationToken cancellationToken)
{
// All entrypoints to this function are Find functions that are only searching
// for specific strings (i.e. they never do a custom search).
Expand Down Expand Up @@ -361,7 +389,8 @@ private static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsyncImpl(
/// <summary>
/// Find the symbols for declarations made in source with the specified name.
/// </summary>
public static Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(Project project, string name, bool ignoreCase, SymbolFilter filter, CancellationToken cancellationToken = default(CancellationToken))
public static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(
Project project, string name, bool ignoreCase, SymbolFilter filter, CancellationToken cancellationToken = default(CancellationToken))
{
if (project == null)
{
Expand All @@ -375,21 +404,22 @@ private static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsyncImpl(

if (string.IsNullOrWhiteSpace(name))
{
return SpecializedTasks.EmptyEnumerable<ISymbol>();
return SpecializedCollections.EmptyEnumerable<ISymbol>();
}

using (Logger.LogBlock(FunctionId.SymbolFinder_Project_Name_FindSourceDeclarationsAsync, cancellationToken))
{
return FindSourceDeclarationsAsyncImpl(project, SearchQuery.Create(name, ignoreCase), filter, cancellationToken);
return await FindSourceDeclarationsAsyncImpl(
project, SearchQuery.Create(name, ignoreCase), filter, cancellationToken).ConfigureAwait(false);
}
}

private static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsyncImpl(
private static async Task<ImmutableArray<ISymbol>> FindSourceDeclarationsAsyncImpl(
Project project, SearchQuery query, SymbolFilter filter, CancellationToken cancellationToken)
{
var list = new List<ISymbol>();
var list = ArrayBuilder<ISymbol>.GetInstance();
await AddDeclarationsAsync(project, query, filter, list, cancellationToken).ConfigureAwait(false);
return list;
return list.ToImmutableAndFree();
}

/// <summary>
Expand Down Expand Up @@ -446,13 +476,15 @@ internal static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(Sol
/// <summary>
/// Find the symbols for declarations made in source with a matching name.
/// </summary>
public static Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(
public static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(
Project project, Func<string, bool> predicate, SymbolFilter filter, CancellationToken cancellationToken = default(CancellationToken))
{
return FindSourceDeclarationsAsync(project, SearchQuery.CreateCustom(predicate), filter, cancellationToken);
return await FindSourceDeclarationsAsync(
project, SearchQuery.CreateCustom(predicate), filter, cancellationToken).ConfigureAwait(false);
}

internal static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(Project project, SearchQuery query, SymbolFilter filter, CancellationToken cancellationToken)
internal static async Task<ImmutableArray<ISymbol>> FindSourceDeclarationsAsync(
Project project, SearchQuery query, SymbolFilter filter, CancellationToken cancellationToken)
{
if (project == null)
{
Expand All @@ -461,26 +493,27 @@ internal static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(Pro

if (query.Name != null && string.IsNullOrWhiteSpace(query.Name))
{
return SpecializedCollections.EmptyEnumerable<ISymbol>();
return ImmutableArray<ISymbol>.Empty;
}

using (Logger.LogBlock(FunctionId.SymbolFinder_Project_Predicate_FindSourceDeclarationsAsync, cancellationToken))
{
var result = new List<ISymbol>();
if (!await project.ContainsSymbolsWithNameAsync(query.GetPredicate(), filter, cancellationToken).ConfigureAwait(false))
{
return result;
return ImmutableArray<ISymbol>.Empty;
}

var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

result.AddRange(FilterByCriteria(compilation.GetSymbolsWithName(query.GetPredicate(), filter, cancellationToken), filter));
return result;
var unfiltered = compilation.GetSymbolsWithName(query.GetPredicate(), filter, cancellationToken).ToImmutableArray();
return FilterByCriteria(unfiltered, filter);
}
}

internal static IEnumerable<ISymbol> FilterByCriteria(IEnumerable<ISymbol> symbols, SymbolFilter criteria)
internal static ImmutableArray<ISymbol> FilterByCriteria(
ImmutableArray<ISymbol> symbols, SymbolFilter criteria)
{
var builder = ArrayBuilder<ISymbol>.GetInstance();
foreach (var symbol in symbols)
{
if (symbol.IsImplicitlyDeclared || symbol.IsAccessor())
Expand All @@ -490,9 +523,16 @@ internal static IEnumerable<ISymbol> FilterByCriteria(IEnumerable<ISymbol> symbo

if (MeetCriteria(symbol, criteria))
{
yield return symbol;
builder.Add(symbol);
}
}

var result = builder.Count == symbols.Length
? symbols
: builder.ToImmutable();

builder.Free();
return result;
}

private static bool MeetCriteria(ISymbol symbol, SymbolFilter filter)
Expand Down
Loading

0 comments on commit 94ef59b

Please sign in to comment.