8364: Memory usage improvements r=jonas-schievink a=alexmaco

These are mostly focused on splitting up enum variants with large size differences between variants by `Box`-ing things up.

In my testing this reduces the memory usage somewhere in the low percentages, even though the measurements are quite noisy.

Co-authored-by: Alexandru Macovei <alexnmaco@gmail.com>
This commit is contained in:
bors[bot] 2021-04-06 13:43:37 +00:00 committed by GitHub
commit 7d39b13996
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 54 additions and 33 deletions

1
Cargo.lock generated
View file

@ -1867,6 +1867,7 @@ name = "vfs"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"fst", "fst",
"indexmap",
"paths", "paths",
"rustc-hash", "rustc-hash",
] ]

View file

@ -322,8 +322,10 @@ impl ExprCollector<'_> {
Vec::new() Vec::new()
}; };
let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); let method_name = e.name_ref().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let generic_args = let generic_args = e
e.generic_arg_list().and_then(|it| GenericArgs::from_ast(&self.ctx(), it)); .generic_arg_list()
.and_then(|it| GenericArgs::from_ast(&self.ctx(), it))
.map(Box::new);
self.alloc_expr( self.alloc_expr(
Expr::MethodCall { receiver, method_name, args, generic_args }, Expr::MethodCall { receiver, method_name, args, generic_args },
syntax_ptr, syntax_ptr,
@ -385,7 +387,7 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::Yield { expr }, syntax_ptr) self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
} }
ast::Expr::RecordExpr(e) => { ast::Expr::RecordExpr(e) => {
let path = e.path().and_then(|path| self.expander.parse_path(path)); let path = e.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
let record_lit = if let Some(nfl) = e.record_expr_field_list() { let record_lit = if let Some(nfl) = e.record_expr_field_list() {
let fields = nfl let fields = nfl
.fields() .fields()
@ -430,7 +432,7 @@ impl ExprCollector<'_> {
} }
ast::Expr::CastExpr(e) => { ast::Expr::CastExpr(e) => {
let expr = self.collect_expr_opt(e.expr()); let expr = self.collect_expr_opt(e.expr());
let type_ref = TypeRef::from_ast_opt(&self.ctx(), e.ty()); let type_ref = Box::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
} }
ast::Expr::RefExpr(e) => { ast::Expr::RefExpr(e) => {
@ -469,8 +471,10 @@ impl ExprCollector<'_> {
arg_types.push(type_ref); arg_types.push(type_ref);
} }
} }
let ret_type = let ret_type = e
e.ret_type().and_then(|r| r.ty()).map(|it| TypeRef::from_ast(&self.ctx(), it)); .ret_type()
.and_then(|r| r.ty())
.map(|it| Box::new(TypeRef::from_ast(&self.ctx(), it)));
let body = self.collect_expr_opt(e.body()); let body = self.collect_expr_opt(e.body());
self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr) self.alloc_expr(Expr::Lambda { args, arg_types, ret_type, body }, syntax_ptr)
} }
@ -755,7 +759,7 @@ impl ExprCollector<'_> {
} }
} }
ast::Pat::TupleStructPat(p) => { ast::Pat::TupleStructPat(p) => {
let path = p.path().and_then(|path| self.expander.parse_path(path)); let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
let (args, ellipsis) = self.collect_tuple_pat(p.fields()); let (args, ellipsis) = self.collect_tuple_pat(p.fields());
Pat::TupleStruct { path, args, ellipsis } Pat::TupleStruct { path, args, ellipsis }
} }
@ -765,7 +769,7 @@ impl ExprCollector<'_> {
Pat::Ref { pat, mutability } Pat::Ref { pat, mutability }
} }
ast::Pat::PathPat(p) => { ast::Pat::PathPat(p) => {
let path = p.path().and_then(|path| self.expander.parse_path(path)); let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
path.map(Pat::Path).unwrap_or(Pat::Missing) path.map(Pat::Path).unwrap_or(Pat::Missing)
} }
ast::Pat::OrPat(p) => { ast::Pat::OrPat(p) => {
@ -779,7 +783,7 @@ impl ExprCollector<'_> {
} }
ast::Pat::WildcardPat(_) => Pat::Wild, ast::Pat::WildcardPat(_) => Pat::Wild,
ast::Pat::RecordPat(p) => { ast::Pat::RecordPat(p) => {
let path = p.path().and_then(|path| self.expander.parse_path(path)); let path = p.path().and_then(|path| self.expander.parse_path(path)).map(Box::new);
let args: Vec<_> = p let args: Vec<_> = p
.record_pat_field_list() .record_pat_field_list()
.expect("every struct should have a field list") .expect("every struct should have a field list")

View file

@ -86,7 +86,7 @@ pub enum Expr {
receiver: ExprId, receiver: ExprId,
method_name: Name, method_name: Name,
args: Vec<ExprId>, args: Vec<ExprId>,
generic_args: Option<GenericArgs>, generic_args: Option<Box<GenericArgs>>,
}, },
Match { Match {
expr: ExprId, expr: ExprId,
@ -106,7 +106,7 @@ pub enum Expr {
expr: Option<ExprId>, expr: Option<ExprId>,
}, },
RecordLit { RecordLit {
path: Option<Path>, path: Option<Box<Path>>,
fields: Vec<RecordLitField>, fields: Vec<RecordLitField>,
spread: Option<ExprId>, spread: Option<ExprId>,
}, },
@ -131,7 +131,7 @@ pub enum Expr {
}, },
Cast { Cast {
expr: ExprId, expr: ExprId,
type_ref: TypeRef, type_ref: Box<TypeRef>,
}, },
Ref { Ref {
expr: ExprId, expr: ExprId,
@ -162,7 +162,7 @@ pub enum Expr {
Lambda { Lambda {
args: Vec<PatId>, args: Vec<PatId>,
arg_types: Vec<Option<TypeRef>>, arg_types: Vec<Option<TypeRef>>,
ret_type: Option<TypeRef>, ret_type: Option<Box<TypeRef>>,
body: ExprId, body: ExprId,
}, },
Tuple { Tuple {
@ -412,13 +412,13 @@ pub enum Pat {
Wild, Wild,
Tuple { args: Vec<PatId>, ellipsis: Option<usize> }, Tuple { args: Vec<PatId>, ellipsis: Option<usize> },
Or(Vec<PatId>), Or(Vec<PatId>),
Record { path: Option<Path>, args: Vec<RecordFieldPat>, ellipsis: bool }, Record { path: Option<Box<Path>>, args: Vec<RecordFieldPat>, ellipsis: bool },
Range { start: ExprId, end: ExprId }, Range { start: ExprId, end: ExprId },
Slice { prefix: Vec<PatId>, slice: Option<PatId>, suffix: Vec<PatId> }, Slice { prefix: Vec<PatId>, slice: Option<PatId>, suffix: Vec<PatId> },
Path(Path), Path(Box<Path>),
Lit(ExprId), Lit(ExprId),
Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> }, Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> },
TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> }, TupleStruct { path: Option<Box<Path>>, args: Vec<PatId>, ellipsis: Option<usize> },
Ref { pat: PatId, mutability: Mutability }, Ref { pat: PatId, mutability: Mutability },
Box { inner: PatId }, Box { inner: PatId },
ConstBlock(ExprId), ConstBlock(ExprId),

View file

@ -289,6 +289,12 @@ impl From<Name> for Path {
} }
} }
impl From<Name> for Box<Path> {
fn from(name: Name) -> Box<Path> {
Box::new(Path::from(name))
}
}
impl From<Name> for ModPath { impl From<Name> for ModPath {
fn from(name: Name) -> ModPath { fn from(name: Name) -> ModPath {
ModPath::from_segments(PathKind::Plain, iter::once(name)) ModPath::from_segments(PathKind::Plain, iter::once(name))

View file

@ -318,7 +318,13 @@ impl<'a> InferenceContext<'a> {
self.normalize_associated_types_in(ret_ty) self.normalize_associated_types_in(ret_ty)
} }
Expr::MethodCall { receiver, args, method_name, generic_args } => self Expr::MethodCall { receiver, args, method_name, generic_args } => self
.infer_method_call(tgt_expr, *receiver, &args, &method_name, generic_args.as_ref()), .infer_method_call(
tgt_expr,
*receiver,
&args,
&method_name,
generic_args.as_deref(),
),
Expr::Match { expr, arms } => { Expr::Match { expr, arms } => {
let input_ty = self.infer_expr(*expr, &Expectation::none()); let input_ty = self.infer_expr(*expr, &Expectation::none());
@ -399,7 +405,7 @@ impl<'a> InferenceContext<'a> {
TyKind::Never.intern(&Interner) TyKind::Never.intern(&Interner)
} }
Expr::RecordLit { path, fields, spread } => { Expr::RecordLit { path, fields, spread } => {
let (ty, def_id) = self.resolve_variant(path.as_ref()); let (ty, def_id) = self.resolve_variant(path.as_deref());
if let Some(variant) = def_id { if let Some(variant) = def_id {
self.write_variant_resolution(tgt_expr.into(), variant); self.write_variant_resolution(tgt_expr.into(), variant);
} }

View file

@ -174,7 +174,7 @@ impl<'a> InferenceContext<'a> {
TyKind::Ref(mutability, static_lifetime(), subty).intern(&Interner) TyKind::Ref(mutability, static_lifetime(), subty).intern(&Interner)
} }
Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat(
p.as_ref(), p.as_deref(),
subpats, subpats,
expected, expected,
default_bm, default_bm,
@ -182,7 +182,7 @@ impl<'a> InferenceContext<'a> {
*ellipsis, *ellipsis,
), ),
Pat::Record { path: p, args: fields, ellipsis: _ } => { Pat::Record { path: p, args: fields, ellipsis: _ } => {
self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) self.infer_record_pat(p.as_deref(), fields, expected, default_bm, pat)
} }
Pat::Path(path) => { Pat::Path(path) => {
// FIXME use correct resolver for the surrounding expression // FIXME use correct resolver for the surrounding expression

View file

@ -14,3 +14,4 @@ rustc-hash = "1.0"
fst = "0.4" fst = "0.4"
paths = { path = "../paths", version = "0.0.0" } paths = { path = "../paths", version = "0.0.0" }
indexmap = "1.6.2"

View file

@ -1,15 +1,22 @@
//! Maps paths to compact integer ids. We don't care about clearings paths which //! Maps paths to compact integer ids. We don't care about clearings paths which
//! no longer exist -- the assumption is total size of paths we ever look at is //! no longer exist -- the assumption is total size of paths we ever look at is
//! not too big. //! not too big.
use rustc_hash::FxHashMap; use std::hash::BuildHasherDefault;
use indexmap::IndexSet;
use rustc_hash::FxHasher;
use crate::{FileId, VfsPath}; use crate::{FileId, VfsPath};
/// Structure to map between [`VfsPath`] and [`FileId`]. /// Structure to map between [`VfsPath`] and [`FileId`].
#[derive(Default)]
pub(crate) struct PathInterner { pub(crate) struct PathInterner {
map: FxHashMap<VfsPath, FileId>, map: IndexSet<VfsPath, BuildHasherDefault<FxHasher>>,
vec: Vec<VfsPath>, }
impl Default for PathInterner {
fn default() -> Self {
Self { map: IndexSet::default() }
}
} }
impl PathInterner { impl PathInterner {
@ -17,7 +24,7 @@ impl PathInterner {
/// ///
/// If `path` does not exists in `self`, returns [`None`]. /// If `path` does not exists in `self`, returns [`None`].
pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> { pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> {
self.map.get(path).copied() self.map.get_index_of(path).map(|i| FileId(i as u32))
} }
/// Insert `path` in `self`. /// Insert `path` in `self`.
@ -25,13 +32,9 @@ impl PathInterner {
/// - If `path` already exists in `self`, returns its associated id; /// - If `path` already exists in `self`, returns its associated id;
/// - Else, returns a newly allocated id. /// - Else, returns a newly allocated id.
pub(crate) fn intern(&mut self, path: VfsPath) -> FileId { pub(crate) fn intern(&mut self, path: VfsPath) -> FileId {
if let Some(id) = self.get(&path) { let (id, _added) = self.map.insert_full(path);
return id; assert!(id < u32::MAX as usize);
} FileId(id as u32)
let id = FileId(self.vec.len() as u32);
self.map.insert(path.clone(), id);
self.vec.push(path);
id
} }
/// Returns the path corresponding to `id`. /// Returns the path corresponding to `id`.
@ -40,6 +43,6 @@ impl PathInterner {
/// ///
/// Panics if `id` does not exists in `self`. /// Panics if `id` does not exists in `self`.
pub(crate) fn lookup(&self, id: FileId) -> &VfsPath { pub(crate) fn lookup(&self, id: FileId) -> &VfsPath {
&self.vec[id.0 as usize] self.map.get_index(id.0 as usize).unwrap()
} }
} }