Skip to content

Commit 1a24cf4

Browse files
Cleanup/perf in creating member maps. (#67997)
* Cleanup/perf in creating member maps. * Simplify * Better assert, and avoid empty arrays * Remove type argument * Remove unnecessary type arguments * Fix * Make static * Add assert
1 parent 62214ae commit 1a24cf4

File tree

4 files changed

+66
-91
lines changed

4 files changed

+66
-91
lines changed

src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -249,19 +249,13 @@ private Dictionary<string, ImmutableArray<NamedTypeSymbol>> GetNameToTypeMembers
249249
{
250250
// NOTE: This method depends on MakeNameToMembersMap() on creating a proper
251251
// NOTE: type of the array, see comments in MakeNameToMembersMap() for details
252-
Interlocked.CompareExchange(ref _nameToTypeMembersMap, getTypesFromMemberMap(GetNameToMembersMap()), null);
252+
Interlocked.CompareExchange(
253+
ref _nameToTypeMembersMap,
254+
ImmutableArrayExtensions.GetTypesFromMemberMap<NamespaceOrTypeSymbol, NamedTypeSymbol>(GetNameToMembersMap(), StringOrdinalComparer.Instance),
255+
comparand: null);
253256
}
254257

255258
return _nameToTypeMembersMap;
256-
257-
static Dictionary<string, ImmutableArray<NamedTypeSymbol>> getTypesFromMemberMap(Dictionary<string, ImmutableArray<NamespaceOrTypeSymbol>> map)
258-
{
259-
#if DEBUG
260-
return ImmutableArrayExtensions.GetTypesFromMemberMap<NamespaceOrTypeSymbol, NamedTypeSymbol, NamespaceSymbol>(map, StringOrdinalComparer.Instance);
261-
#else
262-
return ImmutableArrayExtensions.GetTypesFromMemberMap<NamespaceOrTypeSymbol, NamedTypeSymbol>(map, StringOrdinalComparer.Instance);
263-
#endif
264-
}
265259
}
266260

267261
private Dictionary<string, ImmutableArray<NamespaceOrTypeSymbol>> MakeNameToMembersMap(BindingDiagnosticBag diagnostics)

src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs

Lines changed: 51 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Microsoft.CodeAnalysis.PooledObjects;
1616
using Microsoft.CodeAnalysis.Collections;
1717
using Microsoft.CodeAnalysis.Shared.Collections;
18+
using Roslyn.Utilities;
1819

1920
#if DEBUG
2021
using System.Linq;
@@ -907,107 +908,87 @@ internal static void AddToMultiValueDictionaryBuilder<K, T>(Dictionary<K, object
907908

908909
internal static void CreateNameToMembersMap
909910
<TNamespaceOrTypeSymbol, TNamedTypeSymbol, TNamespaceSymbol>
910-
(Dictionary<string, object> dictionary, Dictionary<String, ImmutableArray<TNamespaceOrTypeSymbol>> result)
911+
(Dictionary<string, object> dictionary, Dictionary<string, ImmutableArray<TNamespaceOrTypeSymbol>> result)
911912
where TNamespaceOrTypeSymbol : class
912913
where TNamedTypeSymbol : class, TNamespaceOrTypeSymbol
913914
where TNamespaceSymbol : class, TNamespaceOrTypeSymbol
914915
{
915-
foreach (var kvp in dictionary)
916-
{
917-
object value = kvp.Value;
918-
ImmutableArray<TNamespaceOrTypeSymbol> members;
916+
foreach (var (name, value) in dictionary)
917+
result.Add(name, createMembers(value));
918+
919+
return;
919920

920-
var builder = value as ArrayBuilder<TNamespaceOrTypeSymbol>;
921-
if (builder != null)
921+
static ImmutableArray<TNamespaceOrTypeSymbol> createMembers(object value)
922+
{
923+
if (value is ArrayBuilder<TNamespaceOrTypeSymbol> builder)
922924
{
923925
Debug.Assert(builder.Count > 1);
924-
bool hasNamespaces = false;
925-
for (int i = 0; i < builder.Count; i++)
926+
foreach (var item in builder)
926927
{
927-
if (builder[i] is TNamespaceSymbol)
928-
{
929-
hasNamespaces = true;
930-
break;
931-
}
928+
if (item is TNamespaceSymbol)
929+
return builder.ToImmutableAndFree();
932930
}
933931

934-
members = hasNamespaces
935-
? builder.ToImmutable()
936-
: ImmutableArray<TNamespaceOrTypeSymbol>.CastUp(builder.ToDowncastedImmutable<TNamedTypeSymbol>());
937-
938-
builder.Free();
932+
return ImmutableArray<TNamespaceOrTypeSymbol>.CastUp(builder.ToDowncastedImmutableAndFree<TNamedTypeSymbol>());
939933
}
940934
else
941935
{
942936
TNamespaceOrTypeSymbol symbol = (TNamespaceOrTypeSymbol)value;
943-
members = symbol is TNamespaceSymbol
944-
? ImmutableArray.Create<TNamespaceOrTypeSymbol>(symbol)
945-
: ImmutableArray<TNamespaceOrTypeSymbol>.CastUp(ImmutableArray.Create<TNamedTypeSymbol>((TNamedTypeSymbol)symbol));
937+
return symbol is TNamespaceSymbol
938+
? ImmutableArray.Create(symbol)
939+
: ImmutableArray<TNamespaceOrTypeSymbol>.CastUp(ImmutableArray.Create((TNamedTypeSymbol)symbol));
946940
}
947-
948-
result.Add(kvp.Key, members);
949941
}
950942
}
951943

952-
internal static Dictionary<string, ImmutableArray<TNamedTypeSymbol>> GetTypesFromMemberMap
953-
<TNamespaceOrTypeSymbol, TNamedTypeSymbol
954-
#if DEBUG
955-
, TNamespaceSymbol
956-
#endif
957-
>
944+
internal static Dictionary<string, ImmutableArray<TNamedTypeSymbol>> GetTypesFromMemberMap<TNamespaceOrTypeSymbol, TNamedTypeSymbol>
958945
(Dictionary<string, ImmutableArray<TNamespaceOrTypeSymbol>> map, IEqualityComparer<string> comparer)
959946
where TNamespaceOrTypeSymbol : class
960947
where TNamedTypeSymbol : class, TNamespaceOrTypeSymbol
961-
#if DEBUG
962-
where TNamespaceSymbol : class, TNamespaceOrTypeSymbol
963-
#endif
964948
{
965949
var dictionary = new Dictionary<string, ImmutableArray<TNamedTypeSymbol>>(comparer);
966950

967-
foreach (var kvp in map)
951+
foreach (var (name, members) in map)
968952
{
969-
ImmutableArray<TNamespaceOrTypeSymbol> members = kvp.Value;
953+
var namedTypes = getOrCreateNamedTypes(members);
954+
if (namedTypes.Length > 0)
955+
dictionary.Add(name, namedTypes);
956+
}
970957

971-
bool hasType = false;
972-
bool hasNamespace = false;
958+
return dictionary;
973959

974-
foreach (var symbol in members)
975-
{
976-
if (symbol is TNamedTypeSymbol)
977-
{
978-
hasType = true;
979-
if (hasNamespace)
980-
{
981-
break;
982-
}
983-
}
984-
else
985-
{
986-
#if DEBUG
987-
Debug.Assert(symbol is TNamespaceSymbol);
988-
#endif
989-
hasNamespace = true;
990-
if (hasType)
991-
{
992-
break;
993-
}
994-
}
995-
}
960+
static ImmutableArray<TNamedTypeSymbol> getOrCreateNamedTypes(ImmutableArray<TNamespaceOrTypeSymbol> members)
961+
{
962+
Debug.Assert(members.Length > 0);
963+
964+
// See if creator 'map' put a downcasted ImmutableArray<TNamedTypeSymbol> in it. If so, we can just directly
965+
// downcast to that and trivially reuse it. If not, that means the array must have contained at least one
966+
// TNamespaceSymbol and we'll need to filter that out.
967+
var membersAsNamedTypes = members.As<TNamedTypeSymbol>();
968+
969+
if (!membersAsNamedTypes.IsDefault)
970+
return membersAsNamedTypes;
971+
972+
// Preallocate the right amount so we can avoid garbage reallocs.
973+
var count = members.Count(static s => s is TNamedTypeSymbol);
996974

997-
if (hasType)
975+
// Must have less items than in the original array. Otherwise, the .As<TNamedTypeSymbol>() cast would
976+
// have succeeded.
977+
Debug.Assert(count < members.Length);
978+
979+
if (count == 0)
980+
return ImmutableArray<TNamedTypeSymbol>.Empty;
981+
982+
var builder = ArrayBuilder<TNamedTypeSymbol>.GetInstance(count);
983+
foreach (var member in members)
998984
{
999-
if (hasNamespace)
1000-
{
1001-
dictionary.Add(kvp.Key, members.OfType<TNamedTypeSymbol>().AsImmutable());
1002-
}
1003-
else
1004-
{
1005-
dictionary.Add(kvp.Key, members.As<TNamedTypeSymbol>());
1006-
}
985+
if (member is TNamedTypeSymbol namedType)
986+
builder.Add(namedType);
1007987
}
1008-
}
1009988

1010-
return dictionary;
989+
Debug.Assert(builder.Count == count);
990+
return builder.ToImmutableAndFree();
991+
}
1011992
}
1012993

1013994
internal static bool SequenceEqual<TElement, TArg>(this ImmutableArray<TElement> array1, ImmutableArray<TElement> array2, TArg arg, Func<TElement, TElement, TArg, bool> predicate)

src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamespaceSymbol.vb

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -186,17 +186,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
186186

187187
' NOTE: This method depends on MakeNameToMembersMap() on creating a proper
188188
' NOTE: type of the array, see comments in MakeNameToMembersMap() for details
189-
190-
Dim dictionary As New Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))
191-
Dim map As Dictionary(Of String, ImmutableArray(Of NamespaceOrTypeSymbol)) = Me.GetNameToMembersMap()
192-
193-
#If DEBUG Then
194-
dictionary = ImmutableArrayExtensions.GetTypesFromMemberMap(Of NamespaceOrTypeSymbol, NamedTypeSymbol, NamespaceSymbol)(map, CaseInsensitiveComparison.Comparer)
195-
#Else
196-
dictionary = ImmutableArrayExtensions.GetTypesFromMemberMap(Of NamespaceOrTypeSymbol, NamedTypeSymbol)(map, CaseInsensitiveComparison.Comparer)
197-
#End If
198-
199-
Interlocked.CompareExchange(_nameToTypeMembersMap, dictionary, Nothing)
189+
Interlocked.CompareExchange(
190+
_nameToTypeMembersMap,
191+
ImmutableArrayExtensions.GetTypesFromMemberMap(Of NamespaceOrTypeSymbol, NamedTypeSymbol)(Me.GetNameToMembersMap(), CaseInsensitiveComparison.Comparer),
192+
comparand:=Nothing)
200193
End If
201194

202195
Return _nameToTypeMembersMap

src/Dependencies/PooledObjects/ArrayBuilder.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,13 @@ public ImmutableArray<U> ToDowncastedImmutable<U>()
322322
return tmp.ToImmutableAndFree();
323323
}
324324

325+
public ImmutableArray<U> ToDowncastedImmutableAndFree<U>() where U : T
326+
{
327+
var result = ToDowncastedImmutable<U>();
328+
this.Free();
329+
return result;
330+
}
331+
325332
/// <summary>
326333
/// Realizes the array and disposes the builder in one operation.
327334
/// </summary>

0 commit comments

Comments
 (0)