Lazily allocate object writer section state - #132923
Open
awakecoding wants to merge 1 commit into
Open
Conversation
Defer symbolic and COFF relocation lists until first use. Store the first section data buffer inline and share common padding buffers to avoid per-section collection allocations.
|
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 reduces fixed per-section allocation overhead in the shared object-writer layer used by NativeAOT tooling by deferring relocation-list and section-data backing allocations until they’re actually needed, while preserving emitted object layout and relocation semantics.
Changes:
- Lazily allocate symbolic relocation lists in
ObjectWriter(usenullto represent “no relocations”, allocateList<SymbolicRelocation>on first add). - Make
SectionDataallocate append buffers / overflow fragment storage only when needed, and share immutable padding buffers for common padding bytes. - Lazily allocate COFF relocation lists by converting
SectionDefinitionto a mutable class with aRelocationsproperty and adding focused regression tests.
File summaries
| File | Description |
|---|---|
| src/coreclr/tools/Common/Compiler/ObjectWriter/SectionData.cs | Lazily allocates append buffer and fragment storage; shares common padding buffers; keeps stream semantics by flushing before read/seek. |
| src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs | Defers per-section symbolic relocation list allocation; skips undefined-symbol scan and relocation emission for null lists. |
| src/coreclr/tools/Common/Compiler/ObjectWriter/CoffObjectWriter.cs | Defers COFF relocation list allocation and updates emission to handle null reloc lists safely. |
| src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj | Adds InternalsVisibleTo for the test project to access internals needed for object-writer tests. |
| src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ObjectWriterTests.cs | Adds targeted tests covering SectionData buffering/padding behavior and COFF relocation/ordering/determinism invariants. |
| src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj | Includes the new ObjectWriterTests.cs in the test project build. |
Review details
- Files reviewed: 6/6 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
These changes form one cohesive optimization on current main: all three allocations are per-section state created by the shared object-writer layer, and all can be deferred for the common section shape without changing object layout.
Motivation
ObjectWritercurrently creates empty symbolic and format-specific relocation lists for every section.SectionDataalso eagerly creates anArrayBufferWriter<byte>, a buffer list, and a padding array even when a section has no buffered writes and only one data fragment.That fixed cost is material for NativeAOT workloads with very large section counts, especially when most sections have no relocations and only one fragment.
Implementation
ObjectWriternow usesnullto represent a section with no symbolic relocations and creates the existingList<SymbolicRelocation>on the first add. Undefined-symbol discovery and format-specific relocation conversion skip this zero-relocation state. Non-empty lists remain ordinary mutable lists, preserving insertion order and platform-specific behavior such as Mach-O's existing in-place reversal.The COFF writer similarly creates
List<CoffRelocation>only when symbolic relocations are converted.SectionDefinitionbecomes a class so this lazily initialized property can be updated without replacing the section record. COFF relocation counts, overflow records, ordering, and emitted bytes are unchanged.SectionDatanow:ArrayBufferWriter<byte>on first buffered write;ReadOnlyMemory<byte>directly;ReadOnlyMemory<byte>ownership and live stream behavior.Object emission remains single-threaded; this does not change its thread-safety contract. No collection capacity planning, compact relocation representation, experiment gate, or profiling infrastructure is included.
Validation
c210d82dbc1ab432b9369604a1caef9a0ab763d2.\build.cmd clr+libs+hostattempt stopped before native compilation because the inherited Windows environment exceededcmd.exe's command-line limit (The input line is too long).N:mapping with a sanitized PATH: 0 warnings, 0 errors..\build.cmd clr.aot+libs -rc Release -lc Release.\build.cmd clr.aot+libs -rc Checked -lc Release.\dotnet.cmd build src\coreclr\tools\aot\ILCompiler.ReadyToRun\ILCompiler.ReadyToRun.csproj -c Release -p:Platform=x64 --no-restore.\dotnet.cmd test src\coreclr\tools\aot\ILCompiler.Compiler.Tests\ILCompiler.Compiler.Tests.csproj -c <Release|Debug|Checked> -p:Platform=x64.\src\tests\build.cmd nativeaot Release tree nativeaot.\src\tests\run.cmd runnativeaottests Releasedotnet format --verify-no-changeschecks andgit diff --checkpassed.Current-main benchmark
The authoritative current-main benchmark uses the repository's net11 toolchain rather than forcing the retained net10 RDM response through an incompatible compiler/framework contract.
An ignored local runner generated 10,000 worker/marker type pairs, compiled them with current-main ILC in multifile mode, and produced 220,076 COFF sections. Baseline and changed compilers were built from the same current-main commit with identical temporary measurement probes. One warmup per variant preceded five measured interleaved A/B pairs.
Object-phase allocation fell in every pair by 37.65-37.83 MiB. Timing and peak process metrics remain sensitive to shared-machine scheduling and GC timing: one object-emission pair regressed 1.6%, while the other four improved. The repeated allocation reduction is the primary current-main signal; timing is directional.
Every measured baseline and changed run emitted the same 49,921,241-byte object with SHA-256
2BDC559EBE496FC9A7237FA938EFB3278429BA0D23D97B4B2146A5E970BA3A32.Retained .NET 10 RDM evidence
The retained production profile used the matching v10.0.11 compiler/framework contract, not current main. That workload emitted a 3,744,336,196-byte COFF BigObj with 3,526,007 sections, 18,258,125 symbols, and tens of millions of relocation records.
For the accepted
lazy-relocation-lists;compact-section-datamechanism:Those net10 results establish the large-scale motivation. The net11 stress workload above is the authoritative validation that the mechanism still applies to current main.
Limitations and risk
Note
This PR description was drafted with GitHub Copilot.