From 0ad3ed0f7947f06711daab05e1d39082df8ea7db Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Mon, 18 Sep 2017 15:03:33 -0700 Subject: [PATCH] back ported only portion that is causing perf issues in dev15.4.x from master. this basically prevent symbol info indexer from creating/calculating same checksum again and again for every documents in the solution. this should make it to do it only once per project rather than document, and only update it when there is a change in the project. --- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 9 ++++-- .../SymbolTree/SymbolTreeInfo_Source.cs | 30 ++++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index c4ac93508a6b9..877b6194b02f2 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -150,9 +150,12 @@ public static Checksum GetMetadataChecksum( { // We can reuse the index for any given reference as long as it hasn't changed. // So our checksum is just the checksum for the PEReference itself. - var serializer = new Serializer(solution.Workspace); - var checksum = serializer.CreateChecksum(reference, cancellationToken); - return checksum; + return ChecksumCache.GetOrCreate(reference, _ => + { + var serializer = new Serializer(solution.Workspace); + var checksum = serializer.CreateChecksum(reference, cancellationToken); + return checksum; + }); } private static Task TryLoadOrCreateMetadataSymbolTreeInfoAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 002edd9d30c83..af4cd77a16f69 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; @@ -41,7 +40,22 @@ public static Task GetInfoForSourceAssemblyAsync( return result; } - public static async Task GetSourceSymbolsChecksumAsync(Project project, CancellationToken cancellationToken) + /// + /// Cache of project to the checksum for it so that we don't have to expensively recompute + /// this each time we get a project. + /// + private static ConditionalWeakTable> s_projectToSourceChecksum = + new ConditionalWeakTable>(); + + public static Task GetSourceSymbolsChecksumAsync(Project project, CancellationToken cancellationToken) + { + var lazy = s_projectToSourceChecksum.GetValue( + project.State, p => new AsyncLazy(c => ComputeSourceSymbolsChecksumAsync(p, c), cacheResult: true)); + + return lazy.GetValueAsync(cancellationToken); + } + + private static async Task ComputeSourceSymbolsChecksumAsync(ProjectState projectState, CancellationToken cancellationToken) { // The SymbolTree for source is built from the source-symbols from the project's compilation's // assembly. Specifically, we only get the name, kind and parent/child relationship of all the @@ -49,14 +63,14 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project // changed. The only thing that can make those source-symbols change in that manner are if // the text of any document changes, or if options for the project change. So we build our // checksum out of that data. - var serializer = new Serializer(project.Solution.Workspace); - var projectStateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var serializer = new Serializer(projectState.LanguageServices.WorkspaceServices); + var projectStateChecksums = await projectState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); // Order the documents by FilePath. Default ordering in the RemoteWorkspace is // to be ordered by Guid (which is not consistent across VS sessions). - var textChecksumsTasks = project.Documents.OrderBy(d => d.FilePath).Select(async d => + var textChecksumsTasks = projectState.DocumentStates.OrderBy(d => d.Value.FilePath).Select(async d => { - var documentStateChecksum = await d.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var documentStateChecksum = await d.Value.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); return documentStateChecksum.Text; }); @@ -96,7 +110,7 @@ internal static async Task CreateSourceSymbolTreeInfoAsync( GenerateSourceNodes(assembly.GlobalNamespace, unsortedNodes, s_getMembersNoPrivate); return CreateSymbolTreeInfo( - project.Solution, checksum, project.FilePath, unsortedNodes.ToImmutableAndFree(), + project.Solution, checksum, project.FilePath, unsortedNodes.ToImmutableAndFree(), inheritanceMap: new OrderPreservingMultiDictionary()); }