Skip to content

Commit

Permalink
Enable pooling for objects in new analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio0694 committed Dec 11, 2024
1 parent a1bfe3d commit 5766465
Showing 1 changed file with 61 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#if ROSLYN_4_12_0_OR_GREATER

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
Expand All @@ -14,6 +15,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.PooledObjects;
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;

namespace CommunityToolkit.Mvvm.SourceGenerators;
Expand All @@ -24,6 +26,22 @@ namespace CommunityToolkit.Mvvm.SourceGenerators;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class UseObservablePropertyOnSemiAutoPropertyAnalyzer : DiagnosticAnalyzer
{
/// <summary>
/// The number of pooled flags per stack (ie. how many properties we expect on average per type).
/// </summary>
private const int NumberOfPooledFlagsPerStack = 20;

/// <summary>
/// Shared pool for <see cref="Dictionary{TKey, TValue}"/> instances.
/// </summary>
[SuppressMessage("MicrosoftCodeAnalysisPerformance", "RS1008", Justification = "This is a pool of (empty) dictionaries, it is not actually storing compilation data.")]
private static readonly ObjectPool<Dictionary<IPropertySymbol, bool[]>> PropertyMapPool = new(static () => new Dictionary<IPropertySymbol, bool[]>(SymbolEqualityComparer.Default));

/// <summary>
/// Shared pool for <see cref="Stack{T}"/>-s of flags, one per type being processed.
/// </summary>
private static readonly ObjectPool<Stack<bool[]>> PropertyFlagsStackPool = new(CreatePropertyFlagsStack);

/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(UseObservablePropertyOnSemiAutoProperty);

Expand Down Expand Up @@ -69,7 +87,8 @@ public override void Initialize(AnalysisContext context)
return;
}

Dictionary<IPropertySymbol, bool[]> propertyMap = new(SymbolEqualityComparer.Default);
Dictionary<IPropertySymbol, bool[]> propertyMap = PropertyMapPool.Allocate();
Stack<bool[]> propertyFlagsStack = PropertyFlagsStackPool.Allocate();

// Crawl all members to discover properties that might be of interest
foreach (ISymbol memberSymbol in typeSymbol.GetMembers())
Expand Down Expand Up @@ -97,8 +116,13 @@ public override void Initialize(AnalysisContext context)
continue;
}

// Take an array from the stack or create a new one otherwise
bool[] flags = propertyFlagsStack.Count > 0
? propertyFlagsStack.Pop()
: new bool[2];

// Track the property for later
propertyMap.Add(propertySymbol, new bool[2]);
propertyMap.Add(propertySymbol, flags);
}

// We want to process both accessors, where we specifically need both the syntax
Expand Down Expand Up @@ -246,6 +270,24 @@ public override void Initialize(AnalysisContext context)
pair.Key.Name));
}
}

// Before clearing the dictionary, move back all values to the stack
foreach (bool[] propertyFlags in propertyMap.Values)
{
// Make sure the array is cleared before returning it
propertyFlags.AsSpan().Clear();

propertyFlagsStack.Push(propertyFlags);
}

// We are now done processing the symbol, we can return the dictionary.
// Note that we must clear it before doing so to avoid leaks and issues.
propertyMap.Clear();

PropertyMapPool.Free(propertyMap);

// Also do the same for the stack, except we don't need to clean it (since it roots no compilation objects)
PropertyFlagsStackPool.Free(propertyFlagsStack);
});
}, SymbolKind.NamedType);
});
Expand Down Expand Up @@ -282,6 +324,23 @@ private static bool TryGetSetPropertyMethodSymbol(INamedTypeSymbol observableObj

return false;
}

/// <summary>
/// Produces a new <see cref="Stack{T}"/> instance to pool.
/// </summary>
/// <returns>The resulting <see cref="Stack{T}"/> instance to use.</returns>
private static Stack<bool[]> CreatePropertyFlagsStack()
{
static IEnumerable<bool[]> EnumerateFlags()
{
for (int i = 0; i < NumberOfPooledFlagsPerStack; i++)
{
yield return new bool[2];
}
}

return new(EnumerateFlags());
}
}

#endif

0 comments on commit 5766465

Please sign in to comment.