From 69293a1d47184897dff9815a98b8be9618305364 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Fri, 4 Oct 2024 15:01:54 -0700 Subject: [PATCH 1/2] Reduce allocations in TypeSymbolExtensions.IsAtLeastAsVisibleAs This method showed up in multiple profiles as about 0.6% of allocations in the Roslyn CA process in the csharp editing speedometer test. This simply avoids those allocations by switching to a static lambda using the arg data to pass in a pooled object. Note that the arg passed into VisitType needed to be a class to allow the ref parameter to change it's value. --- .../Portable/Symbols/TypeSymbolExtensions.cs | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 3e95df90575a1..a0308b0381138 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -14,6 +14,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal static partial class TypeSymbolExtensions { + private static readonly ObjectPool s_visitTypeDataPool + = new ObjectPool(() => new VisitTypeData(), size: 4); + public static bool ImplementsInterface(this TypeSymbol subType, TypeSymbol superInterface, ref CompoundUseSiteInfo useSiteInfo) { foreach (NamedTypeSymbol @interface in subType.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo)) @@ -661,13 +664,34 @@ public static SpecialType GetSpecialTypeSafe(this TypeSymbol? type) return type is object ? type.SpecialType : SpecialType.None; } + private class VisitTypeData + { + public Symbol? Symbol; + public CompoundUseSiteInfo UseSiteInfo; + } + public static bool IsAtLeastAsVisibleAs(this TypeSymbol type, Symbol sym, ref CompoundUseSiteInfo useSiteInfo) { - CompoundUseSiteInfo localUseSiteInfo = useSiteInfo; - var result = type.VisitType((type1, symbol, unused) => IsTypeLessVisibleThan(type1, symbol, ref localUseSiteInfo), sym, - canDigThroughNullable: true); // System.Nullable is public - useSiteInfo = localUseSiteInfo; - return result is null; + var visitTypeData = s_visitTypeDataPool.Allocate(); + + try + { + visitTypeData.UseSiteInfo = useSiteInfo; + visitTypeData.Symbol = sym; + + var result = type.VisitType(static (type1, arg, unused) => IsTypeLessVisibleThan(type1, arg.Symbol!, ref arg.UseSiteInfo), + arg: visitTypeData, + canDigThroughNullable: true); // System.Nullable is public + + useSiteInfo = visitTypeData.UseSiteInfo; + return result is null; + } + finally + { + visitTypeData.UseSiteInfo = default; + visitTypeData.Symbol = null; + s_visitTypeDataPool.Free(visitTypeData); + } } private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref CompoundUseSiteInfo useSiteInfo) From e70d8fac78e331676f08a86aa4484529e3ad9a70 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Tue, 8 Oct 2024 07:44:54 -0700 Subject: [PATCH 2/2] Seal nested class and move to beginning of containing class --- .../CSharp/Portable/Symbols/TypeSymbolExtensions.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index a0308b0381138..4417f2bc0c1f0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -14,6 +14,12 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal static partial class TypeSymbolExtensions { + private sealed class VisitTypeData + { + public Symbol? Symbol; + public CompoundUseSiteInfo UseSiteInfo; + } + private static readonly ObjectPool s_visitTypeDataPool = new ObjectPool(() => new VisitTypeData(), size: 4); @@ -664,12 +670,6 @@ public static SpecialType GetSpecialTypeSafe(this TypeSymbol? type) return type is object ? type.SpecialType : SpecialType.None; } - private class VisitTypeData - { - public Symbol? Symbol; - public CompoundUseSiteInfo UseSiteInfo; - } - public static bool IsAtLeastAsVisibleAs(this TypeSymbol type, Symbol sym, ref CompoundUseSiteInfo useSiteInfo) { var visitTypeData = s_visitTypeDataPool.Allocate();