| File: Language\TagHelperCollection.ChecksumSetPool.cs | Web Access |
| Project: src\roslyn\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using Microsoft.AspNetCore.Razor.PooledObjects; using Microsoft.AspNetCore.Razor.Utilities; namespace Microsoft.AspNetCore.Razor.Language; public abstract partial class TagHelperCollection { private sealed class ChecksumSetPool : CustomObjectPool<HashSet<Checksum>> { // Large projects can have thousands of tag helpers, causing the HashSet to grow well past // the LOH threshold (85KB) during document compilation. With a small retention limit, the // set is trimmed on return and must re-grow (through multiple LOH-allocating resizes) on // every subsequent use. Traces have shown GBs of allocation pressure from this resize churn. // A higher retention limit keeps the grown set in the pool, trading a few MB of stable // potentially LOH memory (across ~20 pool slots, most of which remain empty) for // eliminating repeated resize allocations. The initial capacity is kept smaller so that // small projects don't immediately allocate LOH-sized arrays. #if NET private const int InitialCapacity = 2048; #endif private const int MaximumRetainedCapacity = 16384; public static readonly ChecksumSetPool Default = new(Policy.Instance, DefaultPoolSize); private ChecksumSetPool(PooledObjectPolicy policy, Opt<int> poolSize) : base(policy, poolSize) { } private sealed class Policy : PooledObjectPolicy { public static readonly Policy Instance = new(); private Policy() { } public override HashSet<Checksum> Create() { #if NET return new(capacity: InitialCapacity); #else return []; #endif } public override bool Return(HashSet<Checksum> set) { var count = set.Count; set.Clear(); if (count > MaximumRetainedCapacity) { #if NET9_0_OR_GREATER set.TrimExcess(InitialCapacity); #else set.TrimExcess(); #endif } return true; } } } }