From fccf5178c389831326bb79d3b51b631053c6abd2 Mon Sep 17 00:00:00 2001 From: Todd Grunke Date: Tue, 8 Oct 2024 10:59:18 -0700 Subject: [PATCH] Reduce allocations in TypeSymbolExtensions.IsAtLeastAsVisibleAs (#75401) * 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..4417f2bc0c1f0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -14,6 +14,15 @@ 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); + public static bool ImplementsInterface(this TypeSymbol subType, TypeSymbol superInterface, ref CompoundUseSiteInfo useSiteInfo) { foreach (NamedTypeSymbol @interface in subType.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo)) @@ -663,11 +672,26 @@ public static SpecialType GetSpecialTypeSafe(this TypeSymbol? type) 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)