Avoid boxing NativeAOT dependency enumerators - #132922
Open
awakecoding wants to merge 2 commits 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
agocke
reviewed
Aug 29, 2026
Move concrete-list selection to dependency producers so the analyzer no longer relies on runtime type checks. Preserve the enumerable fallback and cover empty concrete-list results.
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>enumeration without runtime type tests in the analyzer.IEnumerable<T>contract and fallback for all other nodes.Motivation
Dependency analysis exposes dependencies as
IEnumerable<T>, but hot NativeAOT nodes commonly buildDependencyListorList<CombinedDependencyListEntry>. Enumerating those values through the interface boxes collection enumerators.The original profiling experiment observed 28.536 million static concrete-list visits covering 96.861 million entries and 6.715 million conditional-list visits covering 73.188 million entries. A general prototype established the optimization ceiling: graph and mark time each improved by about 7.3 seconds, managed allocation fell by 1.35-1.39 GiB, and output was byte-identical.
Implementation
DependencyNodeCorenow has internal opt-in methods for nodes that naturally own a concrete static or conditional dependency list.ObjectNode,VirtualMethodUseNode,ScannedMethodNode,MethodCodeNode, andEETypeNodeimplement those paths.DependencyAnalyzerasks each producer for its concrete representation and otherwise uses the unchanged enumerable fallback.This keeps representation knowledge with the producer: the analyzer contains no collection casts or runtime type checks. The public
IEnumerable<T>APIs remain unchanged for existing consumers and source-included implementations.The concrete paths use normal
List<T>.Enumeratorvalues, retaining ordering and version checks if anOnMarkedcallback mutates a list during enumeration. Null, already-marked, and deferred conditional dependencies retain their previous behavior.Validation
.\dotnet.cmd test src\coreclr\tools\aot\ILCompiler.Compiler.Tests\ILCompiler.Compiler.Tests.csproj -c Release -p:Platform=x64 -p:UseSharedCompilation=falseILCompilerRelease x64: 0 warnings, 0 errorsILCompiler.ReadyToRunRelease x64: 0 warnings, 0 errors.\src\tests\build.cmd nativeaot Release tree nativeaot /p:UseSharedCompilation=false.\src\tests\run.cmd runnativeaottests ReleaseCurrent-main benchmark
The retained RDM response 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 and ran five interleaved baseline/changed pairs after warmup. Both compilers contained identical temporary measurement probes; no probes or experiment gates are in this change.Short-run wall and phase measurements are within machine noise; the repeatable local signal is lower graph allocation. Every baseline/changed run produced the same 39,922,359-byte object with SHA-256
CE5C81C91A3774A8324455A306F9AE6AE06C30331E7A14D762F94AEEA27CC919.Note
This PR description was drafted with GitHub Copilot.