Shrink the size of Expr (#12610)

# Description
Continuing from #12568, this PR further reduces the size of `Expr` from
64 to 40 bytes. It also reduces `Expression` from 128 to 96 bytes and
`Type` from 32 to 24 bytes.

This was accomplished by:
- for `Expr` with multiple fields (e.g., `Expr::Thing(A, B, C)`),
merging the fields into new AST struct types and then boxing this struct
(e.g. `Expr::Thing(Box<ABC>)`).
- replacing `Vec<T>` with `Box<[T]>` in multiple places. `Expr`s and
`Expression`s should rarely be mutated, if at all, so this optimization
makes sense.

By reducing the size of these types, I didn't notice a large performance
improvement (at least compared to #12568). But this PR does reduce the
memory usage of nushell. My config is somewhat light so I only noticed a
difference of 1.4MiB (38.9MiB vs 37.5MiB).

---------

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
This commit is contained in:
Ian Manske
2024-04-24 15:46:35 +00:00
committed by GitHub
parent c52884b3c8
commit 9996e4a1f8
195 changed files with 688 additions and 601 deletions

View File

@ -100,9 +100,9 @@ pub trait Eval {
Ok(Value::record(record, expr.span))
}
Expr::Table(headers, vals) => {
Expr::Table(table) => {
let mut output_headers = vec![];
for expr in headers {
for expr in table.columns.as_ref() {
let header = Self::eval::<D>(state, mut_state, expr)?.coerce_into_string()?;
if let Some(idx) = output_headers
.iter()
@ -111,7 +111,7 @@ pub trait Eval {
return Err(ShellError::ColumnDefinedTwice {
col_name: header,
second_use: expr.span,
first_use: headers[idx].span,
first_use: table.columns[idx].span,
});
} else {
output_headers.push(header);
@ -119,8 +119,8 @@ pub trait Eval {
}
let mut output_rows = vec![];
for val in vals {
let record = output_headers.iter().zip(val).map(|(col, expr)| {
for val in table.rows.as_ref() {
let record = output_headers.iter().zip(val.as_ref()).map(|(col, expr)| {
Self::eval::<D>(state, mut_state, expr).map(|val| (col.clone(), val))
}).collect::<Result<_,_>>()?;
@ -131,15 +131,15 @@ pub trait Eval {
}
Ok(Value::list(output_rows, expr.span))
}
Expr::Keyword(_, _, expr) => Self::eval::<D>(state, mut_state, expr),
Expr::Keyword(kw) => Self::eval::<D>(state, mut_state, &kw.expr),
Expr::String(s) => Ok(Value::string(s.clone(), expr.span)),
Expr::Nothing => Ok(Value::nothing(expr.span)),
Expr::ValueWithUnit(e, unit) => match Self::eval::<D>(state, mut_state, e)? {
Value::Int { val, .. } => unit.item.build_value(val, unit.span),
Expr::ValueWithUnit(value) => match Self::eval::<D>(state, mut_state, &value.expr)? {
Value::Int { val, .. } => value.unit.item.build_value(val, value.unit.span),
x => Err(ShellError::CantConvert {
to_type: "unit value".into(),
from_type: x.get_type().to_string(),
span: e.span,
span: value.expr.span,
help: None,
}),
},
@ -150,27 +150,27 @@ pub trait Eval {
Expr::Subexpression(block_id) => {
Self::eval_subexpression::<D>(state, mut_state, *block_id, expr.span)
}
Expr::Range(from, next, to, operator) => {
let from = if let Some(f) = from {
Expr::Range(range) => {
let from = if let Some(f) = &range.from {
Self::eval::<D>(state, mut_state, f)?
} else {
Value::nothing(expr.span)
};
let next = if let Some(s) = next {
let next = if let Some(s) = &range.next {
Self::eval::<D>(state, mut_state, s)?
} else {
Value::nothing(expr.span)
};
let to = if let Some(t) = to {
let to = if let Some(t) = &range.to {
Self::eval::<D>(state, mut_state, t)?
} else {
Value::nothing(expr.span)
};
Ok(Value::range(
Range::new(from, next, to, operator.inclusion, expr.span)?,
Range::new(from, next, to, range.operator.inclusion, expr.span)?,
expr.span,
))
}