Skip to content

Investigate additional NativeAOT static initialization and rooting size opportunities #132595

Open

Description

@tannergooding

Summary

Follow-up to #132550 and #132576. A review of all 15 NativeAOT Sizeoscope reports linked from #132576 (comment) found additional cases where static initialization, canonical generic sharing, reflection metadata, or shared representations retain unnecessary code and data.

Priority order: standard/base applications first, representative real-world applications second, and explicitly minimal configurations as supporting evidence. The strongest near-term candidates are Unix ConsolePal, ASP.NET HostString, typed-header generic sharing, and hosting telemetry method normalization. The largest longer-term opportunity is NativeAOT AppContext initialization.

Important

Sizes below are mapped edge-dominated closures, not predicted executable savings. Each change needs a direct before/after NativeAOT publish measurement.

Candidate overview

Priority Candidate Reports / mapped closure Classification
1 Unix ConsolePal terminal metadata 10.4–10.7 KB across Linux Hello/reflection; 5.2–7.4 KB server apps Direct holder change
2 ASP.NET HostString caches 15.6–17.7 KB across all server reports Direct holder change
3 HeaderDictionaryTypeExtensions.GetList<T> 23.8–24.3 KB Web API; ~5 KB Todos API Generic specialization prototype
4 HostingTelemetryHelpers.KnownHttpMethods ~13.5 KB minimal Kestrel; 4.8 KB Web API Direct representation change
5 Linux NetworkChange state 18.2 KB Todos API State separation prototype
6 PathUtils table construction 4.3–5.4 KB across server reports Direct initialization rewrite
7 System.Net.Http.KnownHeaders ~20.9 KB Todos API Architectural representation change
8 UriHelper search tables 11.5–12.3 KB Avalonia Holder/representation prototype
9 HMAC algorithm dispatch 4.9–5.5 KB SHA3-512 closure on Windows Cryptography prototype
10 TimeZoneInfo UTC construction 16.8 KB Avalonia; 4.4–4.9 KB server apps Runtime design investigation
11 NativeAOT AppContext data store 38.1 KB Windows Hello; 15.0 KB Linux Hello Architectural runtime change
12 Logging delegate initialization Repeated 1.5–5.8 KB closures Cross-cutting architectural work

Direct candidates

  • Defer Unix ConsolePal terminal metadata. Move Lazy<TerminalFormatStrings> into a nested holder. Plain Console.WriteLine currently roots TermInfo.DatabaseFactory.ReadActiveDatabase without using terminal formatting.
  • Defer ASP.NET HostString implementation caches. Move s_safeHostStringChars and s_idnMapping into non-reflected nested holders, independently where useful. The current constructor is rooted directly through reflection of the private SearchValues<char> field.
  • Replace HostingTelemetryHelpers.KnownHttpMethods. Reuse HttpMethods canonical comparisons or an equivalent allocation-free helper instead of constructing a FrozenDictionary.
  • Rewrite PathUtils table initialization without LINQ. Both validation tables are used in the server workloads, so holder separation may be neutral; directly construct the small platform-specific sets and measure.
  • Separate independent Task static groups. Give TaskFactory (~120 B), the completed-task singleton (~362 B), and the execution-context callback (~80–126 B) independent holders.

Prototypes requiring measurement

  • Specialize HeaderDictionaryTypeExtensions.GetList<T> for NativeAOT. Canonical System.__Canon sharing causes RequestHeaders.Accept to retain the Set-Cookie parser. Use closed typed parsing entry points or an inlinable type dispatcher.
  • Separate Linux NetworkChange address and availability state. Address-change subscription currently roots availability-only dictionaries, event arguments, callbacks, timer machinery, and handlers.
  • Separate UriHelper whitespace and search tables. Uri.ResolveHelper needs the whitespace array but triggers five SearchValues<char> initializations. Retained escaping methods may still root a subset.
  • Avoid routing HMAC long-key normalization through every SHA implementation. HMACSHA256 -> HMACCommon.ChangeKeyImpl -> SHA3_512.HashData roots SHA3 support initialization. Investigate provider-based hashing using the selected algorithm identifier.
  • Investigate TimeZoneInfo.CreateUtcTimeZone. Many retained methods reference UTC state, so simplifying UTC construction/localization is more promising than only moving the field.
  • Evaluate compact HttpCharacters validation tables. Seven shared SearchValues produce 3.7–4.3 KB closures. Full Kestrel uses most operations, so holders alone are unlikely to help; compact ASCII tests or RVA bitsets require performance validation.

Architectural investigations

  • Avoid eager NativeAOT AppContext materialization. A holder is insufficient because AppContext.GetData is retained. Investigate on-demand native runtime-knob lookup instead of eagerly decoding all keys and values into Dictionary<string, object?>.
  • Separate KnownHeaders name recognition from object materialization. One reflected header getter initializes every KnownHeader and parser. TryGetKnownHeader references every field, so per-field holders alone cannot remove the closure.
  • Reduce eager logging delegate construction. Kestrel, routing, Data Protection, gRPC, and health checks contain many independent LoggerMessage.Define closures. Evaluate source-generated logging or per-event implementations.

Ecosystem follow-ups

  • Avalonia MicroCom registration: the Windows module initializer eagerly registers independent vtables, with individual closures of roughly 3–15 KB. Investigate per-interface registration holders or registration from actual consumers.
  • Avalonia reflection metadata: compiler-generated static-constructor lambdas and broad property-registration graphs are retained. Investigate excluding unnecessary implementation methods and narrowing reflected control/property roots.

Coverage

Category Reports reviewed Result
Standard/base Hello and reflection, Windows and Linux ConsolePal, AppContext, and small Task opportunities
Representative Avalonia, Web API, and Todos API, Windows and Linux Most actionable and architectural candidates above
Minimal Hello and Kestrel on Windows/Linux; WinRT component on Windows Reinforces the same roots; no higher-priority unique candidate

Not currently actionable as holder-only changes

The NativeAOT reflection type-unifier tables are already independently isolated. RuntimeAssemblyInfo, runtime cast/interface caches, processor and array-pool state, calendar/leap-second state, generated JSON/protobuf metadata, and both BCryptAlgorithmCache paths are genuinely used in the examined workloads. Encoding singletons and comparer wrappers are retained by shared runtime selection logic. TextWriter.Null is constrained by its public static field shape.

The reports also contain the Number roots already addressed by #132550 and the CompareInfo/EncodingTable roots addressed by #132576.

Note

This issue was drafted with GitHub Copilot.

Activity

  1. dotnet-policy-service commented on Aug 21, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @dotnet/area-meta
    See info in area-owners.md if you want to be subscribed.

  2. tannergooding commented on Aug 21, 2026

    @tannergooding
    MemberAuthor

    CC. @MichalStrehovsky, @jkotas

    Did some automated analysis over the diffs as a follow up to the other work. Some of this is simpler than others, but most of it looks to be reasonably actionable. There's certainly a few clear "large islands" that are the most impactful and so we can likely whittle away at this for .NET 12

    A few are too small to be meaningful and some may be unactionable as well, we can mark those off or decide to defer as appropriate.

  3. MichalPetryka commented on Aug 21, 2026

    @MichalPetryka
    Contributor

    Kinda related if we want some runtime handling for this: #85014 (comment)

  4. added
    trimming-for-aot`EnableAggressiveTrimming=true` used for running tests with AOT
    on Aug 22, 2026
  5. added this to the 12.0.0 milestone on Aug 22, 2026
  6. jeffhandley commented on Aug 22, 2026

    @jeffhandley
    Member

    @tannergooding I triaged this into the 12.0.0 milestone. Let me know if you were considering this as a quality gate for 11.0.0.

  7. tannergooding commented on Aug 22, 2026

    @tannergooding
    MemberAuthor

    These are generally all pre-existing, so I don't see a significant need to backport or track it for 11.0.

    If any of them are very easy and big enough wins, I think they can be bar checked for backport individually.

  8. removed
    untriagedNew issue has not been triaged by the area owner
    on Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-Metatrimming-for-aot`EnableAggressiveTrimming=true` used for running tests with AOT

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions