From d7ca4eb2141f80d99d30c3db82853b0d60576660 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Wed, 19 Oct 2016 15:53:32 -0700 Subject: [PATCH] implements IProjectCacheHostService in OOP. workspace will cache everything in memory if this service doesn't exist in workspace. now this being exist, workspace will start to put things such as text, tree, compilation either kick out from memory or save it to temporary storage. this should reduce memory foot print once things become idle, but this doesn't help memory footprint while feature is running. with this service, once OOP become idle, memory foot print goes down to about 700MB to 1 GB. but still while code lens run, memory goes up to 3GB. --- src/EditorFeatures/Core/EditorFeatures.csproj | 2 - .../Workspaces/ProjectCacheServiceFactory.cs | 5 ++ src/Features/Core/Portable/Features.csproj | 3 + .../ProjectCacheService.SimpleMRUCache.cs | 3 +- .../Workspace}/ProjectCacheService.cs | 18 +++--- ...ualStudioProjectCacheHostServiceFactory.cs | 5 ++ .../Core/Diagnostics/DiagnosticComputer.cs | 22 ++++++- .../Remote/Core/RemoteWorkspaces.csproj | 2 + .../Core/Services/ChecksumSourceText.cs | 60 +++++++++++++++++++ .../ProjectCacheHostServiceFactory.cs | 20 +++++++ .../Remote/Core/Services/SolutionService.cs | 8 ++- 11 files changed, 130 insertions(+), 18 deletions(-) rename src/{EditorFeatures/Core/Implementation/Workspaces => Features/Core/Portable/Workspace}/ProjectCacheService.SimpleMRUCache.cs (97%) rename src/{EditorFeatures/Core/Implementation/Workspaces => Features/Core/Portable/Workspace}/ProjectCacheService.cs (93%) create mode 100644 src/Workspaces/Remote/Core/Services/ChecksumSourceText.cs create mode 100644 src/Workspaces/Remote/Core/Services/ProjectCacheHostServiceFactory.cs diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 4838a5eb018..839b40f7ec1 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -623,8 +623,6 @@ - - diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs b/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs index e4451e9c98d..dd6872b1a2a 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs +++ b/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs @@ -14,6 +14,11 @@ internal partial class ProjectCacheHostServiceFactory : IWorkspaceServiceFactory public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) { + if (workspaceServices.Workspace.Kind != WorkspaceKind.Host) + { + return new ProjectCacheService(workspaceServices.Workspace); + } + var service = new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); // Also clear the cache when the solution is cleared or removed. diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index b84c78540aa..7caf4e76afa 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -58,6 +58,7 @@ + @@ -681,6 +682,8 @@ + + diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheService.SimpleMRUCache.cs b/src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs similarity index 97% rename from src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheService.SimpleMRUCache.cs rename to src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs index 4b1b83cd98c..7eb65d97d11 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheService.SimpleMRUCache.cs +++ b/src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs @@ -3,12 +3,11 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces +namespace Microsoft.CodeAnalysis.Host { internal partial class ProjectCacheService : IProjectCacheHostService { diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheService.cs b/src/Features/Core/Portable/Workspace/ProjectCacheService.cs similarity index 93% rename from src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheService.cs rename to src/Features/Core/Portable/Workspace/ProjectCacheService.cs index 9a2f2ef87f3..0977d752dec 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheService.cs +++ b/src/Features/Core/Portable/Workspace/ProjectCacheService.cs @@ -6,7 +6,7 @@ using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.Host; -namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces +namespace Microsoft.CodeAnalysis.Host { /// /// This service will implicitly cache previous Compilations used by each supported Workspace implementation. @@ -27,18 +27,18 @@ internal partial class ProjectCacheService : IProjectCacheHostService private readonly SimpleMRUCache _implicitCache; private readonly ImplicitCacheMonitor _implicitCacheMonitor; - + + public ProjectCacheService(Workspace workspace) + { + _workspace = workspace; + } + public ProjectCacheService(Workspace workspace, int implicitCacheTimeout) { _workspace = workspace; - // Only create implicit cache for Visual Studio Workspace (the cost of the - // cache likely outweighs the benefit for the other types of Workspaces). - if (workspace?.Kind == WorkspaceKind.Host) - { - _implicitCache = new SimpleMRUCache(); - _implicitCacheMonitor = new ImplicitCacheMonitor(this, implicitCacheTimeout); - } + _implicitCache = new SimpleMRUCache(); + _implicitCacheMonitor = new ImplicitCacheMonitor(this, implicitCacheTimeout); } public bool IsImplicitCacheEmpty diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs index eee11fa45db..a7689b48bd7 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs @@ -27,6 +27,11 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) private static IWorkspaceService GetMiscProjectCache(HostWorkspaceServices workspaceServices) { + if (workspaceServices.Workspace.Kind != WorkspaceKind.Host) + { + return new ProjectCacheService(workspaceServices.Workspace); + } + var projectCacheService = new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); // Also clear the cache when the solution is cleared or removed. diff --git a/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs b/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs index 1074def9c6b..160ebbcae4a 100644 --- a/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/Core/Diagnostics/DiagnosticComputer.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics.Telemetry; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; @@ -39,6 +40,20 @@ public DiagnosticComputer(Project project) return DiagnosticAnalysisResultMap.Create(ImmutableDictionary.Empty, ImmutableDictionary.Empty); } + var cacheService = _project.Solution.Workspace.Services.GetService(); + using (var cache = cacheService.EnableCaching(_project.Id)) + { + return await AnalyzeAsync(analyzerMap, analyzers, reportSuppressedDiagnostics, logAnalyzerExecutionTime, cancellationToken).ConfigureAwait(false); + } + } + + private async Task> AnalyzeAsync( + BidirectionalMap analyzerMap, + ImmutableArray analyzers, + bool reportSuppressedDiagnostics, + bool logAnalyzerExecutionTime, + CancellationToken cancellationToken) + { var compilation = await _project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); // TODO: can we support analyzerExceptionFilter in remote host? @@ -70,9 +85,10 @@ public DiagnosticComputer(Project project) var builderMap = analysisResult.ToResultBuilderMap(_project, VersionStamp.Default, compilation, analysisResult.Analyzers, cancellationToken); - return DiagnosticAnalysisResultMap.Create(builderMap.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value), - analysisResult.AnalyzerTelemetryInfo.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value), - _exceptions.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value.ToImmutableArray())); + return DiagnosticAnalysisResultMap.Create( + builderMap.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value), + analysisResult.AnalyzerTelemetryInfo.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value), + _exceptions.ToImmutableDictionary(kv => GetAnalyzerId(analyzerMap, kv.Key), kv => kv.Value.ToImmutableArray())); } private void OnAnalyzerException(Exception exception, DiagnosticAnalyzer analyzer, Diagnostic diagnostic) diff --git a/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj b/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj index dcbe941601f..acbdf6c7541 100644 --- a/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj +++ b/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj @@ -68,7 +68,9 @@ + + diff --git a/src/Workspaces/Remote/Core/Services/ChecksumSourceText.cs b/src/Workspaces/Remote/Core/Services/ChecksumSourceText.cs new file mode 100644 index 00000000000..547483fe7d1 --- /dev/null +++ b/src/Workspaces/Remote/Core/Services/ChecksumSourceText.cs @@ -0,0 +1,60 @@ +// 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.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Remote +{ + /// + /// source text that has checksum tied to it + /// + internal class ChecksumSourceText : SourceText + { + private readonly SourceText _sourceText; + + public ChecksumSourceText(Checksum checksum, SourceText sourceText) + { + Checksum = checksum; + _sourceText = sourceText; + } + + public Checksum Checksum { get; } + + public override char this[int position] => _sourceText[position]; + public override Encoding Encoding => _sourceText.Encoding; + public override int Length => _sourceText.Length; + public override SourceTextContainer Container => _sourceText.Container; + + public override SourceText WithChanges(IEnumerable changes) => _sourceText.WithChanges(changes); + public override SourceText GetSubText(TextSpan span) => _sourceText.GetSubText(span); + public override IReadOnlyList GetTextChanges(SourceText oldText) => _sourceText.GetTextChanges(oldText); + public override IReadOnlyList GetChangeRanges(SourceText oldText) => _sourceText.GetChangeRanges(oldText); + + public override void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) => + _sourceText.CopyTo(sourceIndex, destination, destinationIndex, count); + public override void Write(TextWriter writer, TextSpan span, CancellationToken cancellationToken = default(CancellationToken)) => + _sourceText.Write(writer, span, cancellationToken); + + public override bool Equals(object obj) => _sourceText.Equals(obj); + public override int GetHashCode() => _sourceText.GetHashCode(); + + public override string ToString() => _sourceText.ToString(); + public override string ToString(TextSpan span) => _sourceText.ToString(span); + + protected override TextLineCollection GetLinesCore() => _sourceText.Lines; + + protected override bool ContentEqualsImpl(SourceText other) + { + var otherChecksum = other as ChecksumSourceText; + if (otherChecksum != null) + { + return Checksum == otherChecksum.Checksum; + } + + return _sourceText.ContentEquals(other); + } + } +} diff --git a/src/Workspaces/Remote/Core/Services/ProjectCacheHostServiceFactory.cs b/src/Workspaces/Remote/Core/Services/ProjectCacheHostServiceFactory.cs new file mode 100644 index 00000000000..1dd2ccf95f1 --- /dev/null +++ b/src/Workspaces/Remote/Core/Services/ProjectCacheHostServiceFactory.cs @@ -0,0 +1,20 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.Remote +{ + [ExportWorkspaceServiceFactory(typeof(IProjectCacheHostService), ServiceLayer.Host)] + [Shared] + internal partial class ProjectCacheHostServiceFactory : IWorkspaceServiceFactory + { + private const int ImplicitCacheTimeoutInMS = 10000; + + public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) + { + return new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); + } + } +} diff --git a/src/Workspaces/Remote/Core/Services/SolutionService.cs b/src/Workspaces/Remote/Core/Services/SolutionService.cs index 07fd67d687c..b90ff4b1c8a 100644 --- a/src/Workspaces/Remote/Core/Services/SolutionService.cs +++ b/src/Workspaces/Remote/Core/Services/SolutionService.cs @@ -102,7 +102,9 @@ private async Task CreateSolutionAsync(Checksum solutionChecksum, Canc var textLoader = TextLoader.From( TextAndVersion.Create( - await _assetService.GetAssetAsync(documentSnapshot.Text, cancellationToken).ConfigureAwait(false), + new ChecksumSourceText( + documentSnapshot.Text, + await _assetService.GetAssetAsync(documentSnapshot.Text, cancellationToken).ConfigureAwait(false)), VersionStamp.Create(), documentInfo.FilePath)); @@ -155,7 +157,9 @@ private async Task CreateSolutionAsync(Checksum solutionChecksum, Canc var textLoader = TextLoader.From( TextAndVersion.Create( - await _assetService.GetAssetAsync(documentSnapshot.Text, cancellationToken).ConfigureAwait(false), + new ChecksumSourceText( + documentSnapshot.Text, + await _assetService.GetAssetAsync(documentSnapshot.Text, cancellationToken).ConfigureAwait(false)), VersionStamp.Create(), documentInfo.FilePath)); -- GitLab