From ac9474d9a54711083a7ad7da8480c9bbf2687ca9 Mon Sep 17 00:00:00 2001 From: Kevin Pilch Date: Thu, 11 Jan 2018 10:13:19 -0800 Subject: [PATCH] Use a pooled StringBuilder in Dependency.GetID Traces showed this taking upward of 1% of allocations of opening a solution. Fixes #2918. --- .../Tree/Dependencies/Snapshot/Dependency.cs | 25 ++++++++++++++++++- .../Text/StringBuilderExtensions.cs | 19 ++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Snapshot/Dependency.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Snapshot/Dependency.cs index f7681a6b25e..7c135313995 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Snapshot/Dependency.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Snapshot/Dependency.cs @@ -1,17 +1,22 @@ // 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.Concurrent; using System.Collections.Immutable; using System.Globalization; using System.Linq; +using System.Text; using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.CrossTarget; using Microsoft.VisualStudio.ProjectSystem.VS.Utilities; +using Microsoft.VisualStudio.Text; namespace Microsoft.VisualStudio.ProjectSystem.VS.Tree.Dependencies.Snapshot { internal class Dependency : IDependency { + private static ConcurrentBag s_builderPool = new ConcurrentBag(); + // These priorities are for graph nodes only and are used to group graph nodes // appropriatelly in order groups predefined order instead of alphabetically. // Order is not changed for top dependency nodes only for grpah hierarchies. @@ -331,7 +336,25 @@ public static string GetID(ITargetFramework targetFramework, string providerType Requires.NotNullOrEmpty(providerType, nameof(providerType)); Requires.NotNullOrEmpty(modelId, nameof(modelId)); - return $"{targetFramework.ShortName}\\{providerType}\\{Normalize(modelId)}".TrimEnd(CommonConstants.BackSlashDelimiter); + StringBuilder sb = null; + try + { + int length = targetFramework.ShortName.Length + providerType.Length + 2; + if (!s_builderPool.TryTake(out sb)) + { + sb = new StringBuilder(length); + } + + sb.Append(targetFramework.ShortName).Append('\\'); + sb.Append(providerType).Append('\\'); + sb.Append(Normalize(modelId)); + sb.TrimEnd(CommonConstants.BackSlashDelimiter); + return sb.ToString(); + } + finally + { + s_builderPool.Add(sb); + } } private static string GetFullPath(string originalItemSpec, string containingProjectPath) diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/StringBuilderExtensions.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/StringBuilderExtensions.cs index 7029fa0f7d2..54a3be2a059 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/StringBuilderExtensions.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/Text/StringBuilderExtensions.cs @@ -46,5 +46,24 @@ public static void AppendFormat(this StringBuilder builder, StringFormat format) builder.AppendFormat(format.Format, format.Arguments); } } + + public static StringBuilder TrimEnd(this StringBuilder builder, params char[] trimChars) + { + while (builder.Length > 0) + { + foreach (var c in trimChars) + { + if (builder[builder.Length - 1] == c) + { + builder.Length--; + break; + } + + return builder; + } + } + + return builder; + } } }