Skip to content

Commit c0020fd

Browse files
committed
Auto merge of #118490 - Nadrieril:arena-alloc-matrix, r=<try>
[Experiment] Exhaustiveness: allocate memory better Exhaustiveness is a recursive algorithm that allocates a bunch of slices at every step. Let's see if I can improve performance by improving allocations. Already just using `Vec::with_capacity` is showing impressive improvements on my local measurements. r? `@ghost`
2 parents 9bf30eb + eee8baf commit c0020fd

File tree

2 files changed

+45
-37
lines changed

2 files changed

+45
-37
lines changed

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::usefulness::{
55

66
use crate::errors::*;
77

8-
use rustc_arena::TypedArena;
8+
use rustc_arena::{DroplessArena, TypedArena};
99
use rustc_ast::Mutability;
1010
use rustc_data_structures::fx::FxHashSet;
1111
use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -29,13 +29,15 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
2929
let (thir, expr) = tcx.thir_body(def_id)?;
3030
let thir = thir.borrow();
3131
let pattern_arena = TypedArena::default();
32+
let dropless_arena = DroplessArena::default();
3233
let mut visitor = MatchVisitor {
3334
tcx,
3435
thir: &*thir,
3536
param_env: tcx.param_env(def_id),
3637
lint_level: tcx.local_def_id_to_hir_id(def_id),
3738
let_source: LetSource::None,
3839
pattern_arena: &pattern_arena,
40+
dropless_arena: &dropless_arena,
3941
error: Ok(()),
4042
};
4143
visitor.visit_expr(&thir[expr]);
@@ -80,6 +82,7 @@ struct MatchVisitor<'thir, 'p, 'tcx> {
8082
lint_level: HirId,
8183
let_source: LetSource,
8284
pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
85+
dropless_arena: &'p DroplessArena,
8386
/// Tracks if we encountered an error while checking this body. That the first function to
8487
/// report it stores it here. Some functions return `Result` to allow callers to short-circuit
8588
/// on error, but callers don't need to store it here again.
@@ -302,6 +305,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
302305
param_env: self.param_env,
303306
module: self.tcx.parent_module(self.lint_level).to_def_id(),
304307
pattern_arena: self.pattern_arena,
308+
dropless_arena: self.dropless_arena,
305309
match_lint_level: self.lint_level,
306310
match_span,
307311
scrut_span,

compiler/rustc_mir_build/src/thir/pattern/usefulness.rs

+40-36
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ use crate::errors::{
499499

500500
use rustc_data_structures::captures::Captures;
501501

502-
use rustc_arena::TypedArena;
502+
use rustc_arena::{DroplessArena, TypedArena};
503503
use rustc_data_structures::stack::ensure_sufficient_stack;
504504
use rustc_hir::def_id::DefId;
505505
use rustc_hir::HirId;
@@ -508,8 +508,8 @@ use rustc_session::lint;
508508
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
509509
use rustc_span::{Span, DUMMY_SP};
510510

511-
use smallvec::{smallvec, SmallVec};
512-
use std::fmt;
511+
use smallvec::SmallVec;
512+
use std::{fmt, iter::once};
513513

514514
pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
515515
pub(crate) tcx: TyCtxt<'tcx>,
@@ -521,6 +521,7 @@ pub(crate) struct MatchCheckCtxt<'p, 'tcx> {
521521
pub(crate) module: DefId,
522522
pub(crate) param_env: ty::ParamEnv<'tcx>,
523523
pub(crate) pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
524+
pub(crate) dropless_arena: &'p DroplessArena,
524525
/// Lint level at the match.
525526
pub(crate) match_lint_level: HirId,
526527
/// The span of the whole match, if applicable.
@@ -570,13 +571,12 @@ impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
570571
/// Represents a pattern-tuple under investigation.
571572
#[derive(Clone)]
572573
struct PatStack<'p, 'tcx> {
573-
// Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well.
574-
pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>,
574+
pats: &'p [&'p DeconstructedPat<'p, 'tcx>],
575575
}
576576

577577
impl<'p, 'tcx> PatStack<'p, 'tcx> {
578-
fn from_pattern(pat: &'p DeconstructedPat<'p, 'tcx>) -> Self {
579-
PatStack { pats: smallvec![pat] }
578+
fn from_pattern(cx: &MatchCheckCtxt<'p, '_>, pat: &'p DeconstructedPat<'p, 'tcx>) -> Self {
579+
PatStack { pats: cx.dropless_arena.alloc_from_iter(once(pat)) }
580580
}
581581

582582
fn is_empty(&self) -> bool {
@@ -597,11 +597,14 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
597597

598598
// Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is
599599
// an or-pattern. Panics if `self` is empty.
600-
fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> {
601-
self.head().flatten_or_pat().into_iter().map(move |pat| {
602-
let mut new_pats = smallvec![pat];
603-
new_pats.extend_from_slice(&self.pats[1..]);
604-
PatStack { pats: new_pats }
600+
fn expand_or_pat<'a>(
601+
&'a self,
602+
cx: &'a MatchCheckCtxt<'p, 'tcx>,
603+
) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> {
604+
self.head().flatten_or_pat().into_iter().map(move |pat| PatStack {
605+
pats: cx
606+
.dropless_arena
607+
.alloc_from_iter(once(pat).chain(self.pats[1..].iter().copied())),
605608
})
606609
}
607610

@@ -614,9 +617,13 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
614617
) -> PatStack<'p, 'tcx> {
615618
// We pop the head pattern and push the new fields extracted from the arguments of
616619
// `self.head()`.
617-
let mut new_pats = self.head().specialize(pcx, ctor);
618-
new_pats.extend_from_slice(&self.pats[1..]);
619-
PatStack { pats: new_pats }
620+
let ctor_fields = self.head().specialize(pcx, ctor);
621+
PatStack {
622+
pats: pcx
623+
.cx
624+
.dropless_arena
625+
.alloc_from_iter(ctor_fields.into_iter().chain(self.pats[1..].iter().copied())),
626+
}
620627
}
621628
}
622629

@@ -667,8 +674,11 @@ impl<'p, 'tcx> MatrixRow<'p, 'tcx> {
667674

668675
// Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is
669676
// an or-pattern. Panics if `self` is empty.
670-
fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = MatrixRow<'p, 'tcx>> + Captures<'a> {
671-
self.pats.expand_or_pat().map(|patstack| MatrixRow {
677+
fn expand_or_pat<'a>(
678+
&'a self,
679+
cx: &'a MatchCheckCtxt<'p, 'tcx>,
680+
) -> impl Iterator<Item = MatrixRow<'p, 'tcx>> + Captures<'a> {
681+
self.pats.expand_or_pat(cx).map(|patstack| MatrixRow {
672682
pats: patstack,
673683
parent_row: self.parent_row,
674684
is_under_guard: self.is_under_guard,
@@ -711,7 +721,7 @@ impl<'p, 'tcx> fmt::Debug for MatrixRow<'p, 'tcx> {
711721
/// the matrix will correspond to `scrutinee.0.Some.0` and the second column to `scrutinee.1`.
712722
#[derive(Clone)]
713723
struct Matrix<'p, 'tcx> {
714-
rows: Vec<MatrixRow<'p, 'tcx>>,
724+
rows: SmallVec<[MatrixRow<'p, 'tcx>; 8]>,
715725
/// Stores an extra fictitious row full of wildcards. Mostly used to keep track of the type of
716726
/// each column. This must obey the same invariants as the real rows.
717727
wildcard_row: PatStack<'p, 'tcx>,
@@ -720,10 +730,10 @@ struct Matrix<'p, 'tcx> {
720730
impl<'p, 'tcx> Matrix<'p, 'tcx> {
721731
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
722732
/// expands it. Internal method, prefer [`Matrix::new`].
723-
fn expand_and_push(&mut self, row: MatrixRow<'p, 'tcx>) {
733+
fn expand_and_push(&mut self, cx: &MatchCheckCtxt<'p, 'tcx>, row: MatrixRow<'p, 'tcx>) {
724734
if !row.is_empty() && row.head().is_or_pat() {
725735
// Expand nested or-patterns.
726-
for new_row in row.expand_or_pat() {
736+
for new_row in row.expand_or_pat(cx) {
727737
self.rows.push(new_row);
728738
}
729739
} else {
@@ -732,25 +742,18 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
732742
}
733743

734744
/// Build a new matrix from an iterator of `MatchArm`s.
735-
fn new<'a>(
736-
cx: &MatchCheckCtxt<'p, 'tcx>,
737-
iter: impl Iterator<Item = &'a MatchArm<'p, 'tcx>>,
738-
scrut_ty: Ty<'tcx>,
739-
) -> Self
740-
where
741-
'p: 'a,
742-
{
745+
fn new(cx: &MatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>) -> Self {
743746
let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP));
744-
let wildcard_row = PatStack::from_pattern(wild_pattern);
745-
let mut matrix = Matrix { rows: vec![], wildcard_row };
746-
for (row_id, arm) in iter.enumerate() {
747+
let wildcard_row = PatStack::from_pattern(cx, wild_pattern);
748+
let mut matrix = Matrix { rows: SmallVec::with_capacity(arms.len()), wildcard_row };
749+
for (row_id, arm) in arms.iter().enumerate() {
747750
let v = MatrixRow {
748-
pats: PatStack::from_pattern(arm.pat),
751+
pats: PatStack::from_pattern(cx, arm.pat),
749752
parent_row: row_id, // dummy, we won't read it
750753
is_under_guard: arm.has_guard,
751754
reachable: false,
752755
};
753-
matrix.expand_and_push(v);
756+
matrix.expand_and_push(cx, v);
754757
}
755758
matrix
756759
}
@@ -806,11 +809,12 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
806809
ctor: &Constructor<'tcx>,
807810
) -> Matrix<'p, 'tcx> {
808811
let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor);
809-
let mut matrix = Matrix { rows: vec![], wildcard_row };
812+
let rows = SmallVec::with_capacity(self.rows.len()); // Better waste capacity than reallocate a lot.
813+
let mut matrix = Matrix { rows, wildcard_row };
810814
for (i, row) in self.rows().enumerate() {
811815
if ctor.is_covered_by(pcx, row.head().ctor()) {
812816
let new_row = row.pop_head_constructor(pcx, ctor, i);
813-
matrix.expand_and_push(new_row);
817+
matrix.expand_and_push(pcx.cx, new_row);
814818
}
815819
}
816820
matrix
@@ -1386,7 +1390,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
13861390
arms: &[MatchArm<'p, 'tcx>],
13871391
scrut_ty: Ty<'tcx>,
13881392
) -> UsefulnessReport<'p, 'tcx> {
1389-
let mut matrix = Matrix::new(cx, arms.iter(), scrut_ty);
1393+
let mut matrix = Matrix::new(cx, arms, scrut_ty);
13901394
let non_exhaustiveness_witnesses =
13911395
compute_exhaustiveness_and_reachability(cx, &mut matrix, true);
13921396

0 commit comments

Comments
 (0)