From 25557c47b6b18a09abc18dc2ba071cf9e4823729 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Sun, 3 Aug 2025 21:09:10 -0700 Subject: [PATCH 1/2] ExtensionGroupingInfo - reduce consumed memory and delay creation of Cci adapter instances until emit --- .../Symbols/Source/ExtensionGroupingInfo.cs | 89 ++++++++++--------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs index cbf28d711b716..219614f03b8bd 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs @@ -20,16 +20,13 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal sealed class ExtensionGroupingInfo { - /// - /// Extension block symbols declared in a class are grouped by their corresponding grouping type metadata name (top level key), - /// then grouped by their corresponding extension marker type metadata name (the secondary key used by MultiDictionary). - /// s are the extension blocks. - /// - private readonly Dictionary> _groupingMap; - private ImmutableArray _lazyGroupingTypes; + private readonly ImmutableArray _groupingTypes; public ExtensionGroupingInfo(SourceMemberContainerTypeSymbol container) { + // Extension block symbols declared in a class are grouped by their corresponding grouping type metadata name (top level key), + // then grouped by their corresponding extension marker type metadata name (the secondary key used by MultiDictionary). + // s are the extension blocks. var groupingMap = new Dictionary>(EqualityComparer.Default); foreach (var type in container.GetTypeMembers("")) @@ -53,26 +50,21 @@ public ExtensionGroupingInfo(SourceMemberContainerTypeSymbol container) markerMap.Add(sourceNamedType.ExtensionMarkerName, sourceNamedType); } - _groupingMap = groupingMap; - } + var builder = ArrayBuilder.GetInstance(groupingMap.Count); - public ImmutableArray GetGroupingTypes() - { - if (_lazyGroupingTypes.IsDefault) + foreach (KeyValuePair> pair in groupingMap) { - var builder = ArrayBuilder.GetInstance(_groupingMap.Count); - - foreach (KeyValuePair> pair in _groupingMap) - { - builder.Add(new ExtensionGroupingType(pair.Key, pair.Value)); - } + builder.Add(new ExtensionGroupingType(pair.Key, pair.Value)); + } - builder.Sort(); + builder.Sort(); - ImmutableInterlocked.InterlockedInitialize(ref _lazyGroupingTypes, builder.ToImmutableAndFree()); - } + _groupingTypes = builder.ToImmutableAndFree(); + } - return ImmutableArray.CastUp(_lazyGroupingTypes); + public ImmutableArray GetGroupingTypes() + { + return ImmutableArray.CastUp(_groupingTypes); } public Cci.ITypeDefinition GetCorrespondingMarkerType(SynthesizedExtensionMarker markerMethod) @@ -83,13 +75,12 @@ public Cci.ITypeDefinition GetCorrespondingMarkerType(SynthesizedExtensionMarker private ExtensionMarkerType GetCorrespondingMarkerType(SourceNamedTypeSymbol extension) { Debug.Assert(extension.IsExtension); - GetGroupingTypes(); // Tracked by https://github.com/dotnet/roslyn/issues/78827 : Optimize lookup with side dictionaries? var groupingName = extension.ExtensionGroupingName; var markerName = extension.ExtensionMarkerName; - foreach (var groupingType in _lazyGroupingTypes) + foreach (var groupingType in _groupingTypes) { if (groupingType.Name != groupingName) { @@ -144,12 +135,11 @@ public Cci.TypeMemberVisibility GetCorrespondingMarkerMethodVisibility(Synthesiz public Cci.ITypeDefinition GetCorrespondingGroupingType(SourceNamedTypeSymbol extension) { Debug.Assert(extension.IsExtension); - GetGroupingTypes(); // Tracked by https://github.com/dotnet/roslyn/issues/78827 : Optimize lookup with a side dictionary? var groupingName = extension.ExtensionGroupingName; - foreach (var groupingType in _lazyGroupingTypes) + foreach (var groupingType in _groupingTypes) { if (groupingType.Name == groupingName) { @@ -174,8 +164,7 @@ internal ImmutableArray GetMergedExtensions(SourceNamedTy /// internal IEnumerable> EnumerateMergedExtensionBlocks() { - GetGroupingTypes(); - foreach (var groupingType in _lazyGroupingTypes) + foreach (var groupingType in _groupingTypes) { foreach (var markerType in groupingType.ExtensionMarkerTypes) { @@ -384,7 +373,7 @@ private sealed class ExtensionGroupingType : ExtensionGroupingOrMarkerType, ICom { private readonly string _name; public readonly ImmutableArray ExtensionMarkerTypes; - private readonly ImmutableArray _typeParameters; + private ImmutableArray _lazyTypeParameters; public ExtensionGroupingType(string name, MultiDictionary extensionMarkerTypes) { @@ -399,10 +388,6 @@ public ExtensionGroupingType(string name, MultiDictionary new ExtensionGroupingTypeTypeParameter(@this, p), this) : - []; } int IComparable.CompareTo(ExtensionGroupingType? other) @@ -411,7 +396,21 @@ int IComparable.CompareTo(ExtensionGroupingType? other) return ExtensionMarkerTypes[0].CompareTo(other.ExtensionMarkerTypes[0]); } - protected override IEnumerable GenericParameters => _typeParameters; + protected override IEnumerable GenericParameters + { + get + { + if (_lazyTypeParameters.IsDefault) + { + var typeParameters = ExtensionMarkerTypes[0].UnderlyingExtensions[0].Arity != 0 ? + ((INestedTypeDefinition)ExtensionMarkerTypes[0].UnderlyingExtensions[0].GetCciAdapter()).GenericParameters.SelectAsArray(static (p, @this) => new ExtensionGroupingTypeTypeParameter(@this, p), this) : + []; + ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameters, typeParameters); + } + + return _lazyTypeParameters; + } + } protected override ushort GenericParameterCount => (ushort)ExtensionMarkerTypes[0].UnderlyingExtensions[0].Arity; @@ -534,7 +533,7 @@ private sealed class ExtensionMarkerType : ExtensionGroupingOrMarkerType, ICompa public readonly ExtensionGroupingType GroupingType; private readonly string _name; public readonly ImmutableArray UnderlyingExtensions; - private readonly ImmutableArray _typeParameters; + private ImmutableArray _lazyTypeParameters; public ExtensionMarkerType(ExtensionGroupingType groupingType, string name, MultiDictionary.ValueSet extensions) { @@ -545,10 +544,6 @@ public ExtensionMarkerType(ExtensionGroupingType groupingType, string name, Mult builder.AddRange(extensions); builder.Sort(LexicalOrderSymbolComparer.Instance); UnderlyingExtensions = builder.ToImmutableAndFree(); - - _typeParameters = UnderlyingExtensions[0].Arity != 0 ? - ((INestedTypeDefinition)UnderlyingExtensions[0].GetCciAdapter()).GenericParameters.SelectAsArray(static (p, @this) => new InheritedTypeParameter(p.Index, @this, p), this) : - []; } public int CompareTo(ExtensionMarkerType? other) @@ -557,7 +552,21 @@ public int CompareTo(ExtensionMarkerType? other) return LexicalOrderSymbolComparer.Instance.Compare(UnderlyingExtensions[0], other.UnderlyingExtensions[0]); } - protected override IEnumerable GenericParameters => _typeParameters; + protected override IEnumerable GenericParameters + { + get + { + if (_lazyTypeParameters.IsDefault) + { + var typeParameters = UnderlyingExtensions[0].Arity != 0 ? + ((INestedTypeDefinition)UnderlyingExtensions[0].GetCciAdapter()).GenericParameters.SelectAsArray(static (p, @this) => new InheritedTypeParameter(p.Index, @this, p), this) : + []; + ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameters, typeParameters); + } + + return _lazyTypeParameters; + } + } protected override ushort GenericParameterCount => (ushort)UnderlyingExtensions[0].Arity; From f53136d6bd094d89d58cae8418f7bf3fe0f3ce3d Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 7 Aug 2025 17:28:54 -0700 Subject: [PATCH 2/2] PR feedback --- .../CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs index 6aea9d8271035..9d1f2c5224adb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs @@ -27,7 +27,7 @@ public ExtensionGroupingInfo(SourceMemberContainerTypeSymbol container) { // Extension block symbols declared in a class are grouped by their corresponding grouping type metadata name (top level key), // then grouped by their corresponding extension marker type metadata name (the secondary key used by MultiDictionary). - // s are the extension blocks. + // SourceNamedTypeSymbols are the extension blocks. var groupingMap = new Dictionary>(EqualityComparer.Default); foreach (var type in container.GetTypeMembers(""))