Use hash-based string table reservations - #132932
Open
awakecoding wants to merge 3 commits into
Open
Conversation
Replace per-insertion tree ordering with hash-based deduplication and restore ordinal order once before suffix sorting. This preserves object bytes while reducing reservation CPU. Add COFF coverage for suffix sharing, duplicate names, UTF-8 boundaries, section and symbol interactions, repeated flushes, and determinism.
|
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.
🟡 Changes recommended
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the NativeAOT/shared object-writer StringTableBuilder to use hash-based deduplication for reserved strings, then restores a deterministic ordinal ordering once at flush-time before the existing suffix-sharing sort/emission. It also adds COFF-focused tests to validate deterministic bytes/offsets and key edge cases.
Changes:
- Replace
SortedSet<Utf8String>reservations withHashSet<Utf8String>deduplication and add an explicitArray.Sortstep before the existing suffix sort. - Add
InternalsVisibleToso tests can access the required internals for COFF string-table validation. - Add new COFF string-table tests covering section/symbol naming behaviors, duplicates, suffix sharing, determinism across reservation orders, and multi-batch/repeated write scenarios.
File summaries
| File | Description |
|---|---|
| src/coreclr/tools/Common/Compiler/ObjectWriter/StringTableBuilder.cs | Switch reservation storage to HashSet and add an explicit ordinal pre-sort before suffix sorting. |
| src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj | Add InternalsVisibleTo for ILCompiler.Compiler.Tests. |
| src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj | Include the new CoffStringTableTests.cs in compilation items. |
| src/coreclr/tools/aot/ILCompiler.Compiler.Tests/CoffStringTableTests.cs | Add targeted tests asserting COFF string-table bytes/offsets and determinism. |
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Lite
jkotas
reviewed
Aug 29, 2026
Skip empty string-table flushes and remove the dedicated white-box tests in favor of the existing functional object-writer coverage.
Member
|
/azp run runtime-nativeaot-outerloop |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
jkotas
reviewed
Aug 30, 2026
jkotas
approved these changes
Aug 30, 2026
Member
|
/azp run runtime-nativeaot-outerloop |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
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
Motivation
COFF object emission reserves every symbol name before writing the symbol table. A
SortedSetpays tree insertion and comparison costs for every reservation, including duplicates, even though ordering is only needed when the table is flushed.Retained full-RDM measurements on the matching .NET 10.0.11 compiler showed that hash-based reservation reduced the isolated
coff-string-reservephase from 7.66-8.59 seconds to 4.63 seconds: a 3-4 second, 40-46% targeted CPU reduction. The output object and whole-process managed allocation were effectively unchanged. End-to-end wall time varied too much on the shared machine to establish a whole-build improvement, so this change is presented only as a targeted object-writer optimization.Implementation
StringTableBuildernow:Utf8Stringvalues in aHashSet<Utf8String>.Utf8String.CompareTo, which is ordinal byte ordering and matches the previousSortedSetenumeration.Utf8String.Equals,GetHashCode, andCompareToall operate on the same UTF-8 byte content, so hash deduplication and the former tree deduplication have the same equality semantics. The explicit ordinal sort removes hash enumeration order from the output before suffix sharing assigns offsets.The builder is shared by the COFF, ELF, Mach-O, and PE export writers. This change intentionally preserves its existing ordering and emission contract for every consumer; the measured workload and new integration coverage target the Windows COFF path.
No experiment gate, profiler, capacity planning, object-writer representation change, or other NativeAOT optimization is included.
Validation
The following commands were run on Windows x64:
ILCompiler.Compiler.Tests: 22 passed, 0 failed on the final diff.ILCompiler.ReadyToRun.Tests: 85 passed and 5 platform tests skipped after excluding two local PDB tests that fail before object writing because the DIA COM object is unavailable (externalComObjectis null).Long build commands used a temporary short
substdrive and a sanitized inherited build environment to avoid the Windowsvcvarscommand-line length limit. No source change was needed for that environment issue.Current-main A/B benchmark
The port was measured at base commit
d2ea726dd3d8faad797755c9fbb1ff15226e25f0with an uncommitted stress probe that calls the actualStringTableBuilderfrom baseline and changedILCompiler.Compiler.dllbuilds.The workload creates 500,000 deterministic suffix-sharing families: 1.5 million distinct UTF-8 names and 3.5 million reservation calls, including duplicate and non-ASCII names. It emits a 21,484,379-byte string-table blob rather than a giant object. Four runs per side were interleaved in
A B B A A B B Aorder. Values below are medians.SortedSetbaselineHashSet+ ordinal sortEvery individual run improved the targeted reservation phase:
The sort cost moves from each tree insertion to the first symbol-offset lookup, explaining the symbol-path increase. The string-table write difference is only a few milliseconds and is not treated as meaningful.
The isolated probe makes hash bucket allocation visible: it allocates 37.9 MiB more while retaining 22.8 MiB less peak working set. In the retained full-RDM compiler measurement, the allocation difference was only 15.2 MiB out of 115.4 GiB (+0.013%), which is effectively neutral at whole-compiler scale.
Retained RDM evidence
The retained .NET 10.0.11 full-RDM experiment used the same compiler build for controls and the gated variant, changing only
string-table-hashset.5B3E5823E9BE816DACD4D47311449E20F0F8233FC5A853439BC30F1F2A351B3DControls varied from 605 to 819 seconds end to end. That variance is much larger than this optimization, so no whole-build wall-time claim is made.
Byte identity
F2531F67CF2F990CC4ED39DC51BF1BA2ACF926195149A49FCCBC9F4AF5921946, identical offsets and checksum.809013362C3A5F6E708D2BD354EC38C94E5DEFB11AC92E8D0D0306F482DD8B93.Limitations
Note
This pull request description was generated with GitHub Copilot.