Implement "small substs optimization" for substs of length 1 #58310
Labels
C-enhancement
Category: An issue proposing an enhancement or a PR with one.
C-optimization
Category: An issue highlighting optimization opportunities or PRs implementing such
E-mentor
Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
I-compiletime
Issue: Problems and improvements with respect to compile times.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
One of the core types in the compiler is
ty::Subst
, which represents a list of "substitutions" - arguments to generic parameters. For example,Option<u32>
has aSubsts
of[Uint(u32)]
andT: Sized
has aSubsts
of[Param(T)]
.A
ty::Substs
is an interned&'tcx List<Kind<'tcx>>
. It is represented as a thin pointer to a struct of this form:Where a
List
of length 0 is represented by this bit of evil code:And
List
s of non-zero length are represented by aList
that references an interner. This is inefficient, because I'm quite sure that manySubst
s are of length 1 (e.g., consider all the trait bounds of traits with no type parameter).When we look into the interner, there are 2 problems:
Potential for measurements
It might be nice to count the amount of
Substs
of each length while compiling popular crates. This should be rather easy if you modify the interner.Implementation Strategy
Stage 1 - getting a newtype
There are a few representations for
Substs
that are possible, but in any case I think the best way to go forward would be to first hide the representation.Currently a
Substs
is this typedef:And it is normally used as
&'tcx Substs<'tcx>
.We would need to replace it with something that has a hidden representation, i.e. initially a newtype of the form:
I think a reasonable way of going around this would be to first have a
Then retire the use of the old
Substs
typedef in most places, then switchSubstsRef
to be a newtype.When using a newtype, it's probably a good idea to have a
Deref
impl of this form:This will avoid needing to implement specific methods in most cases.
Also remember to implement the "derive" trait impls (
Hash
,PartialEq
, etc.) as needed.Stage 2 - improving the representation
My preferred representation is as follows:
Use the first 2 bits as the tag, in the style of [
TAG_MASK
], e.g.Then represent things as follows:
A substs of length 0:
List::empty() | LIST_TAG
A substs of a single type:
ty | TYPE_TAG
A substs of a single region:
ty | REGION_TAG
A substs of length >1:
substs | LIST_TAG
You'll want to define
Substs
as follows:Then you can implement
Deref
in this style:Then you'll basically only need to change the interning functions not to go through the interner for substs of length 1. As long as you always consistently map substs of length 1 to not use an interner, you can do
PartialEq
,Hash
etc. "by value".Then you can "cut the ribbon" and do a
@bors try
to see how much perf you gain.Stage 2a - Cleanup
Remove occurrences of
InternalSubsts
in comments.Items for later investigation
It might be worth investigating whether it's worth extending the small substs optimization to substs of length 2 to get rid of even more interner calls - that would increase the size of
TyS
and things, so it might be a space-time tradeoff.The text was updated successfully, but these errors were encountered: