Repository navigation
Overhaul span compression - #163857
Overhaul span compression#163857nnethercote wants to merge 1 commit into
Conversation
|
@bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Overhaul span compression
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (d2adbd4): comparison URL. Overall result: ❌✅ regressions and improvements - please read:Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf. Next, please: If you can, justify the regressions found in this try perf run in writing along with @bors rollup=never rustc-perf Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary -1.5%, secondary -1.1%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary -3.5%, secondary -3.5%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeThis perf run didn't have relevant results for this metric. Bootstrap: 489.693s -> 492.198s (0.51%) |
|
icounts results look good. Cycles results are even better, probably because max-rss/faults/cache-misses/branch-misses are all improved. |
|
LLM disclosure: I used an LLM for some of the ideas and analysis in this PR. I wrote the code and text myself. |
302cea2 to
1596682
Compare
7d10c65 to
e40a818
Compare
|
I made some small changes that shouldn't affect perf, let's check: @bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Overhaul span compression
This comment has been minimized.
This comment has been minimized.
|
Finished benchmarking commit (03aba7c): comparison URL. Overall result: ❌✅ regressions and improvements - please read:Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf. Next, please: If you can, justify the regressions found in this try perf run in writing along with @bors rollup=never rustc-perf Instruction countOur most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.
Max RSS (memory usage)Results (primary -1.3%, secondary -1.0%)A less reliable metric. May be of interest, but not used to determine the overall result above.
CyclesResults (primary -3.6%, secondary -0.6%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Binary sizeResults (primary -0.1%, secondary -0.1%)A less reliable metric. May be of interest, but not used to determine the overall result above.
Bootstrap: 489.893s -> 492.261s (0.48%) |
|
New perf results are basically identical to the old ones. |
|
In addition to the performance optimization, the new design also seems much more understandable, and it's proving to be a good base to build on for further optimizations. |
|
I did some rough measurements today.
|
| len_with_tag_or_marker: u16, | ||
| ctxt_or_parent_or_marker: u16, | ||
| } | ||
| #[repr(packed(4))] |
There was a problem hiding this comment.
Does #[repr(packed(1))] improve anything further?
Since we are using unaligned accesses anyway, why not try reducing the span-containing types further (probably not in this PR though).
IIRC, we tried this in the past with the old encoding, and it didn't change much.
There was a problem hiding this comment.
I tried it, it's not worth it.
- Instruction counts on benchmarks were slightly worse (< 1%) on some benchmarks.
librustc_driver.sosize increased by 1.28 MiB.- It shrunk a few AST and HIR nodes, but none of the important ones. Here's the diff:
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 4c7ae87556f..9d932c7df62 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -4473,15 +4473,15 @@ mod size_asserts {
static_assert_size!(Expr, 64);
static_assert_size!(ExprKind, 32);
static_assert_size!(FieldDef, 80);
- static_assert_size!(Fn, 192);
+ static_assert_size!(Fn, 184);
static_assert_size!(FnDecl, 24);
- static_assert_size!(FnHeader, 76);
- static_assert_size!(FnSig, 96);
+ static_assert_size!(FnHeader, 72);
+ static_assert_size!(FnSig, 88);
static_assert_size!(ForeignItem, 72);
static_assert_size!(ForeignItemKind, 16);
static_assert_size!(GenericArg, 24);
static_assert_size!(GenericArgs, 40);
- static_assert_size!(GenericBound, 80);
+ static_assert_size!(GenericBound, 72);
static_assert_size!(GenericParam, 80);
static_assert_size!(Generics, 40);
static_assert_size!(Impl, 80);
@@ -4489,7 +4489,7 @@ mod size_asserts {
static_assert_size!(ItemKind, 88);
static_assert_size!(Lifetime, 16);
static_assert_size!(LitKind, 24);
- static_assert_size!(Local, 96);
+ static_assert_size!(Local, 88);
static_assert_size!(MetaItem, 80);
static_assert_size!(MetaItemKind, 40);
static_assert_size!(MetaItemLit, 40);
@@ -4502,7 +4502,7 @@ mod size_asserts {
static_assert_size!(QSelf, 24);
static_assert_size!(Stmt, 32);
static_assert_size!(StmtKind, 16);
- static_assert_size!(TraitImplHeader, 64);
+ static_assert_size!(TraitImplHeader, 56);
static_assert_size!(Ty, 56);
static_assert_size!(TyKind, 40);
// tidy-alphabetical-end
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index dfcb8871fe9..1f668e7007b 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -5299,7 +5299,7 @@ mod size_asserts {
static_assert_size!(Res, 12);
static_assert_size!(Stmt<'_>, 32);
static_assert_size!(StmtKind<'_>, 16);
- static_assert_size!(TraitImplHeader<'_>, 48);
+ static_assert_size!(TraitImplHeader<'_>, 40);
static_assert_size!(TraitItem<'_>, 88);
static_assert_size!(TraitItemKind<'_>, 48);
static_assert_size!(Ty<'_>, 48);
Currently there are span representations if you have a context and no
parent, or a parent and no context. But if you have both a context *and*
a parent, the span is always interned. In incremental builds this is
something like 20-40% of all spans.
This commit uses a new compression scheme that gives small perf wins for
non-incremental and bigger perf wins for incremental. The following table
briefly summarizes the formats.
```
-----------------------------------------------------------------
old new
-----------------------------------------------------------------
InlineCtxt lo32,len15,ctxt16,parent0 lo32,len15,ctxt16,parent0
InlineParent lo32,len15,ctxt0,parent16 lo32,len14,ctxt0,parent15
InlinePair n/a lo24,len7,ctxt15,parent16
PartialInt. ctxt16 everything else
FullInt. everything else n/a
```
Things to note.
- The old scheme had a 32-bit field and two 16-bit fields. The new scheme has
one 64-bit field and uses `repr(packed(4))`. (Keeping the alignment at
4 is important to keep AST node sizes low.)
- The new scheme distinguishes the four formats with a single variable-length
prefix in the high bits. This is simpler than the old combination of tag bits
and special values.
- The new `SpanField` type is used to describe the various field widths
for each format, and provide operations on them.
- The new `InlineParent` is very slightly worse than the old one, with one less
bit for each of len and parent. This doesn't affect many spans.
- The new `InlinePair` format handles most cases where both a context and a
parent are present. It has to compromise some on `lo` and `len`, but still
gets a lot of them. This is the big performance win.
- The partially/fully interned cases are merged, because the Interned format
can now always hold a full context inline, and the new `Interned` is
equivalent to the old PartiallyInterned format. (This requires
limiting ctxts to 29 bits, but we'll hit other problems, such as OOM,
long before that limit is reached.) This also means we don't need to
store the ctxt field in the interner, and we use the new
`SpanDataNoCtxt` type for that.
e40a818 to
d25eebb
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
I addressed all the suggestions except for @rustbot ready |
View all comments
Currently there are span representations if you have a context and no parent, or a parent and no context. But if you have both a context and a parent, the span is always interned. In incremental builds this is something like 20-40% of all spans.
This commit uses a new compression scheme that gives small perf wins for non-incremental and bigger perf wins for incremental. The following table briefly summarizes the formats.
Things to note.
The old scheme had a 32-bit field and two 16-bit fields. The new scheme has one 64-bit field and uses
repr(packed(4)). (Keeping the alignment at 4 is important to keep AST node sizes low.)The new scheme distinguishes the four formats with a single variable-length prefix in the high bits. This is simpler than the old combination of tag bits and special values.
The new
SpanFieldtype is used to describe the various field widths for each format, and provide operations on them.The new
InlineParentis very slightly worse than the old one, with one less bit for each of len and parent. This doesn't affect many spans.The new
InlinePairformat handles most cases where both a context and a parent are present. It has to compromise some onloandlen, but still gets a lot of them. This is the big performance win.The partially/fully interned cases are merged, because the Interned format can now always hold a full context inline, and the new
Internedis equivalent to the old PartiallyInterned format. (This requires limiting ctxts to 29 bits, but we'll hit other problems, such as OOM, long before that limit is reached.) This also means we don't need to store the ctxt field in the interner, and we use the newSpanDataNoCtxttype for that.r? @petrochenkov