From 1bba8770bdafa13224e85864bb8628497d0b87ea Mon Sep 17 00:00:00 2001 From: Pharring Date: Mon, 24 Mar 2014 16:12:07 -0700 Subject: [PATCH] MetadataHelpers.SplitQualifiedName interllay uses string.Split. This results in an allocation. The allocation is immediately converted to an ImmutableArray. This change replaces string.Split with a hand-rolled algorithm that tries to avoid allocations. First, zero or 1 length arrays avoid any intermediate allocations. For larger results, the exact length is precomputed and we try to get an ArrayBuilder from the pool. As a bonus, since "System" is such a frequent first string, we avoid a new substring allocation. (changeset 1213621) --- .../Symbols/MissingMetadataTypeSymbol.cs | 2 +- .../Source/MetadataReader/MetadataHelpers.cs | 70 +++++++++++++++++-- .../Source/MetadataReader/MetadataTypeName.cs | 11 +-- .../Symbols/MissingMetadataTypeSymbol.vb | 2 +- .../Source/VisualBasicCompilationOptions.vb | 2 +- 5 files changed, 69 insertions(+), 18 deletions(-) diff --git a/Src/Compilers/CSharp/Source/Symbols/MissingMetadataTypeSymbol.cs b/Src/Compilers/CSharp/Source/Symbols/MissingMetadataTypeSymbol.cs index 4d1c0e18cf3..7e369eaeca9 100644 --- a/Src/Compilers/CSharp/Source/Symbols/MissingMetadataTypeSymbol.cs +++ b/Src/Compilers/CSharp/Source/Symbols/MissingMetadataTypeSymbol.cs @@ -206,7 +206,7 @@ public override Symbol ContainingSymbol if (namespaceName.Length > 0) { - string[] namespaces = MetadataHelpers.SplitQualifiedName(namespaceName); + var namespaces = MetadataHelpers.SplitQualifiedName(namespaceName); int i; for (i = 0; i < namespaces.Length; i++) diff --git a/Src/Compilers/Core/Source/MetadataReader/MetadataHelpers.cs b/Src/Compilers/Core/Source/MetadataReader/MetadataHelpers.cs index dc2802dd931..d71cd6bea25 100644 --- a/Src/Compilers/Core/Source/MetadataReader/MetadataHelpers.cs +++ b/Src/Compilers/Core/Source/MetadataReader/MetadataHelpers.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; @@ -19,6 +20,7 @@ internal static class MetadataHelpers public const string GenericTypeNameManglingString = "`"; public const int MaxStringLengthForParamSize = 22; public const int MaxStringLengthForIntToStringConversion = 22; + public const string SystemString = "System"; internal struct AssemblyQualifiedTypeName { @@ -486,12 +488,63 @@ internal static string UnmangleMetadataNameForArity(string emittedTypeName, int return emittedTypeName; } - internal static string[] SplitQualifiedName( - string pstrName) + /// + /// An ImmutableArray representing the single string "System" + /// + private static readonly ImmutableArray SplitQualifiedNameSystem = ImmutableArray.Create(SystemString); + + internal static ImmutableArray SplitQualifiedName( + string name) { - Debug.Assert(pstrName != null); + Debug.Assert(name != null); + + if (name.Length == 0) + { + return ImmutableArray.Empty; + } + + // PERF: Avoid String.Split because of the allocations. Also, we can special-case + // for "System" if it is the first or only part. + + int dots = 0; + foreach (char ch in name) + { + if (ch == DotDelimiter) + { + dots++; + } + } + + if (dots == 0) + { + return name == SystemString ? SplitQualifiedNameSystem : ImmutableArray.Create(name); + } - return pstrName.Split(DotDelimiter); + var result = ArrayBuilder.GetInstance(dots + 1); + + int start = 0; + for (int i = 0; dots > 0; i++) + { + if (name[i] == DotDelimiter) + { + int len = i - start; + if (len == 6 && start == 0 && name.StartsWith(SystemString)) + { + result.Add(SystemString); + } + else + { + result.Add(name.Substring(start, len)); + } + + dots--; + start = i + 1; + } + } + + result.Add(name.Substring(start)); + + return result.ToImmutableAndFree(); } internal static string SplitQualifiedName( @@ -508,7 +561,14 @@ internal static string UnmangleMetadataNameForArity(string emittedTypeName, int return pstrName; } - qualifier = pstrName.Substring(0, delimiter); + if (delimiter == 6 && pstrName.StartsWith(SystemString)) + { + qualifier = SystemString; + } + else + { + qualifier = pstrName.Substring(0, delimiter); + } return pstrName.Substring(delimiter + 1); } diff --git a/Src/Compilers/Core/Source/MetadataReader/MetadataTypeName.cs b/Src/Compilers/Core/Source/MetadataReader/MetadataTypeName.cs index 971a67f9b9f..3c711517f2e 100644 --- a/Src/Compilers/Core/Source/MetadataReader/MetadataTypeName.cs +++ b/Src/Compilers/Core/Source/MetadataReader/MetadataTypeName.cs @@ -273,16 +273,7 @@ public ImmutableArray NamespaceSegments { if (namespaceSegments.IsDefault) { - string nsName = NamespaceName; - - if (nsName.Length == 0) - { - namespaceSegments = ImmutableArray.Empty; - } - else - { - namespaceSegments = MetadataHelpers.SplitQualifiedName(nsName).AsImmutableOrNull(); - } + namespaceSegments = MetadataHelpers.SplitQualifiedName(NamespaceName); } return namespaceSegments; diff --git a/Src/Compilers/VisualBasic/Source/Symbols/MissingMetadataTypeSymbol.vb b/Src/Compilers/VisualBasic/Source/Symbols/MissingMetadataTypeSymbol.vb index 2cde8ca7253..e7e16451f7c 100644 --- a/Src/Compilers/VisualBasic/Source/Symbols/MissingMetadataTypeSymbol.vb +++ b/Src/Compilers/VisualBasic/Source/Symbols/MissingMetadataTypeSymbol.vb @@ -129,7 +129,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Dim container As NamespaceSymbol = m_ContainingModule.GlobalNamespace If m_NamespaceName.Length > 0 Then - Dim namespaces() As String = MetadataHelpers.SplitQualifiedName(m_NamespaceName) + Dim namespaces = MetadataHelpers.SplitQualifiedName(m_NamespaceName) Dim i As Integer For i = 0 To namespaces.Length - 1 Step 1 diff --git a/Src/Compilers/VisualBasic/Source/VisualBasicCompilationOptions.vb b/Src/Compilers/VisualBasic/Source/VisualBasicCompilationOptions.vb index dcf8c94f82c..568fa3ea13d 100644 --- a/Src/Compilers/VisualBasic/Source/VisualBasicCompilationOptions.vb +++ b/Src/Compilers/VisualBasic/Source/VisualBasicCompilationOptions.vb @@ -324,7 +324,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return ImmutableArray(Of String).Empty End If - Return MetadataHelpers.SplitQualifiedName(_rootNamespace).AsImmutableOrNull() + Return MetadataHelpers.SplitQualifiedName(_rootNamespace) End Function ''' -- GitLab