mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 14:21:44 +00:00
Merge #8364
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:
commit
7d39b13996
8 changed files with 54 additions and 33 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
]
|
]
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue