mirror of
https://github.com/nushell/nushell.git
synced 2025-06-30 22:50:14 +02:00
LazyRecord (#7619)
This is an attempt to implement a new `Value::LazyRecord` variant for performance reasons. `LazyRecord` is like a regular `Record`, but it's possible to access individual columns without evaluating other columns. I've implemented `LazyRecord` for the special `$nu` variable; accessing `$nu` is relatively slow because of all the information in `scope`, and [`$nu` accounts for about 2/3 of Nu's startup time on Linux](https://github.com/nushell/nushell/issues/6677#issuecomment-1364618122). ### Benchmarks I ran some benchmarks on my desktop (Linux, 12900K) and the results are very pleasing. Nu's time to start up and run a command (`cargo build --release; hyperfine 'target/release/nu -c "echo \"Hello, world!\""' --shell=none --warmup 10`) goes from **8.8ms to 3.2ms, about 2.8x faster**. Tests are also much faster! Running `cargo nextest` (with our very slow `proptest` tests disabled) goes from **7.2s to 4.4s (1.6x faster)**, because most tests involve launching a new instance of Nu. ### Design (updated) I've added a new `LazyRecord` trait and added a `Value` variant wrapping those trait objects, much like `CustomValue`. `LazyRecord` implementations must implement these 2 functions: ```rust // All column names fn column_names(&self) -> Vec<&'static str>; // Get 1 specific column value fn get_column_value(&self, column: &str) -> Result<Value, ShellError>; ``` ### Serializability `Value` variants must implement `Serializable` and `Deserializable`, which poses some problems because I want to use unserializable things like `EngineState` in `LazyRecord`s. To work around this, I basically lie to the type system: 1. Add `#[typetag::serde(tag = "type")]` to `LazyRecord` to make it serializable 2. Any unserializable fields in `LazyRecord` implementations get marked with `#[serde(skip)]` 3. At the point where a `LazyRecord` normally would get serialized and sent to a plugin, I instead collect it into a regular `Value::Record` (which can be serialized)
This commit is contained in:
@ -136,6 +136,10 @@ pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
|
||||
}
|
||||
nu_json::Value::Object(m)
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let collected = val.collect()?;
|
||||
value_to_json_value(&collected)?
|
||||
}
|
||||
Value::CustomValue { val, .. } => val.to_json(),
|
||||
})
|
||||
}
|
||||
|
@ -185,6 +185,10 @@ pub fn value_to_string(v: &Value, span: Span) -> Result<String, ShellError> {
|
||||
}
|
||||
Ok(format!("{{{}}}", collection.join(", ")))
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let collected = val.collect()?;
|
||||
value_to_string(&collected, span)
|
||||
}
|
||||
// All strings outside data structures are quoted because they are in 'command position'
|
||||
// (could be mistaken for commands by the Nu parser)
|
||||
Value::String { val, .. } => Ok(escape_quote_string(val)),
|
||||
|
@ -141,6 +141,10 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
||||
.map(|(x, y)| format!("{}: {}", x, local_into_string(y.clone(), ", ", config)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(separator),
|
||||
Value::LazyRecord { val, .. } => match val.collect() {
|
||||
Ok(val) => local_into_string(val, separator, config),
|
||||
Err(error) => format!("{:?}", error),
|
||||
},
|
||||
Value::Block { val, .. } => format!("<Block {}>", val),
|
||||
Value::Closure { val, .. } => format!("<Closure {}>", val),
|
||||
Value::Nothing { .. } => String::new(),
|
||||
|
@ -61,6 +61,10 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result<toml::Value, ShellErr
|
||||
}
|
||||
toml::Value::Table(m)
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let collected = val.collect()?;
|
||||
helper(engine_state, &collected)?
|
||||
}
|
||||
Value::List { vals, .. } => toml::Value::Array(toml_list(engine_state, vals)?),
|
||||
Value::Block { span, .. } => {
|
||||
let code = engine_state.get_span_contents(span);
|
||||
|
@ -62,6 +62,10 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
|
||||
}
|
||||
serde_yaml::Value::Mapping(m)
|
||||
}
|
||||
Value::LazyRecord { val, .. } => {
|
||||
let collected = val.collect()?;
|
||||
value_to_yaml_value(&collected)?
|
||||
}
|
||||
Value::List { vals, .. } => {
|
||||
let mut out = vec![];
|
||||
|
||||
|
Reference in New Issue
Block a user