dotnet / dotnet/runtime

Investigate additional NativeAOT static initialization and rooting size opportunities

Open
#132,595 5 comments 1 reaction 0 assignees View on GitHub
area-Meta trimming-for-aot
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

## Summary

Follow-up to #132550 and #132576. A review of all 15 NativeAOT Sizeoscope reports linked from https://github.com/dotnet/runtime/pull/132576#issuecomment-5362869103 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` | 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` 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` 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` 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` 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`.
- [ ] **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.

Contributor guide

Open the contributing guide

Research direction

Start by selecting one of the prioritized candidates, then reproduce its relevant NativeAOT Sizeoscope report and inspect the named symbol or initialization path. Compare NativeAOT publish measurements before and after the investigation; done means a measured reduction in the targeted retained code or data without regressing the workload.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
compilers, performance
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.