Avoid boxing NativeAOT dependency enumerators - #132922
Open
awakecoding wants to merge 1 commit into
Open
Conversation
Enumerate known concrete dependency lists and arrays directly while preserving custom enumerable and mutation semantics. Add focused coverage for collection shapes, ordering, conditional dependencies, and mutation.
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib |
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
Pull request overview
This PR updates the NativeAOT dependency analysis “mark” walk to avoid allocator churn when static/conditional dependencies are provided as common concrete collections but exposed via IEnumerable<T>.
Changes:
- Adds fast paths in
DependencyAnalyzer.GetStaticDependenciesImplfor exactDependencyList, exactList<T>, and arrays to avoid boxing/allocation of enumerators. - Extracts conditional dependency handling into a shared helper (
ProcessConditionalDependency) while keeping existing conditional/deferred semantics. - Adds targeted unit tests covering ordering, fallback enumeration behavior (including
List<T>subclasses that reimplementIEnumerable<T>), and mutation detection.
File summaries
| File | Description |
|---|---|
| src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework/DependencyAnalyzer.cs | Adds exact-type fast paths for list/array dependency enumeration and factors conditional dependency handling into a helper. |
| src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj | Includes the new DependencyAnalyzerTests.cs in the explicit compile item list. |
| src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyAnalyzerTests.cs | Adds coverage for ordering, fallback enumeration semantics, null/unconditional condition handling, and mutation detection. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Lite
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
IEnumerable<T>.List<T>subclass enumeration and list mutation detection by restricting the fast path to exact known types and using the concreteList<T>.Enumerator.Motivation
Dependency analysis exposes dependencies as
IEnumerable<T>, but the hot paths commonly receiveDependencyList,List<CombinedDependencyListEntry>, or arrays. Enumerating those values through the interface boxes collection enumerators.Profiling a large production NativeAOT desktop application found:
Against the midpoint of adjacent controls, direct concrete iteration reduced both graph and mark time by about 7.3 seconds and managed allocation by 1.35-1.39 GiB. The generated object was byte-identical. This was the strongest repeatable compiler wall-phase improvement among 15 tested hypotheses.
Implementation
DependencyAnalyzernow recognizes the exact built-in list types and dependency-entry arrays before falling back to the existingIEnumerable<T>loops. Exact-type guards ensure aList<T>subclass that reimplementsIEnumerable<T>retains its custom behavior.The list paths intentionally use the concrete struct enumerator rather than
CollectionsMarshal.AsSpan. This removes boxing while retaining the existingList<T>version checks if anOnMarkedcallback mutates the collection during enumeration.Conditional dependency handling is extracted into a shared helper without changing null-condition, already-marked, or deferred-condition behavior.
Validation
.\build.cmd clr.aot+libs -rc Release -lc Release.\dotnet.cmd test src\coreclr\tools\aot\ILCompiler.Compiler.Tests\ILCompiler.Compiler.Tests.csproj -c Release -p:Platform=x64DependencyList, iterator fallback, customList<T>enumeration, empty/single/multiple entries, immediate/deferred/null conditions, ordering, and mutation detection.\src\tests\build.cmd nativeaot Release tree nativeaot.\src\tests\run.cmd runnativeaottests ReleaseCurrent-main benchmark
I did not reuse the retained RDM response because it is pinned to
net10.0, WindowsDesktop 10.0.11, and the matching .NET 10 framework closure, while current main builds anet11.0compiler and framework. Mixing those contracts would not be a valid comparison.Instead, an ignored local runner generated a current-main
net11.0workload with 10,000 worker/marker type pairs, used the current-main ILC response shape, and ran two interleaved batches after warmup. Both compilers contained the same temporary measurement probes; no probes or experiment gates are in this change.Short-run wall and phase measurements were sensitive to machine load; the consistently repeated signal was lower graph allocation. The full-scale profile above provides the stronger graph/mark timing evidence.
Every same-path baseline/changed pair produced a byte-identical 39,922,334-byte object. Batch 1 used SHA-256
5CAE603CDA1F795CFF49DEC7B9BAEFDDF2422C5D33A0CD52E49E229F7F21353E; batch 2 usedD8BCA75F1759EA5FF0B3E585CAA4C14F81A5ED04165044A2DC5063712880CE97. Identity is evaluated within each batch because the response and output paths differ between batch roots.Note
This PR description was drafted with GitHub Copilot.