mirror of
https://github.com/nushell/nushell.git
synced 2025-06-30 06:30:08 +02:00
Copy-on-write for record values (#12305)
# Description This adds a `SharedCow` type as a transparent copy-on-write pointer that clones to unique on mutate. As an initial test, the `Record` within `Value::Record` is shared. There are some pretty big wins for performance. I'll post benchmark results in a comment. The biggest winner is nested access, as that would have cloned the records for each cell path follow before and it doesn't have to anymore. The reusability of the `SharedCow` type is nice and I think it could be used to clean up the previous work I did with `Arc` in `EngineState`. It's meant to be a mostly transparent clone-on-write that just clones on `.to_mut()` or `.into_owned()` if there are actually multiple references, but avoids cloning if the reference is unique. # User-Facing Changes - `Value::Record` field is a different type (plugin authors) # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib` # After Submitting - [ ] use for `EngineState` - [ ] use for `Value::List`
This commit is contained in:
@ -13,6 +13,7 @@ bench = false
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.92.3" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.92.3" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.92.3" }
|
||||
|
||||
bincode = "1.3"
|
||||
rmp-serde = "1.1"
|
||||
|
@ -1,10 +1,13 @@
|
||||
use std::{cmp::Ordering, sync::Arc};
|
||||
use std::cmp::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
plugin::{PluginInterface, PluginSource},
|
||||
util::with_custom_values_in,
|
||||
};
|
||||
use nu_protocol::{ast::Operator, CustomValue, IntoSpanned, ShellError, Span, Spanned, Value};
|
||||
use nu_utils::SharedCow;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(test)]
|
||||
@ -28,7 +31,7 @@ mod tests;
|
||||
#[doc(hidden)]
|
||||
pub struct PluginCustomValue {
|
||||
#[serde(flatten)]
|
||||
shared: SerdeArc<SharedContent>,
|
||||
shared: SharedCow<SharedContent>,
|
||||
|
||||
/// Which plugin the custom value came from. This is not defined on the plugin side. The engine
|
||||
/// side is responsible for maintaining it, and it is not sent over the serialization boundary.
|
||||
@ -152,11 +155,11 @@ impl PluginCustomValue {
|
||||
source: Option<Arc<PluginSource>>,
|
||||
) -> PluginCustomValue {
|
||||
PluginCustomValue {
|
||||
shared: SerdeArc(Arc::new(SharedContent {
|
||||
shared: SharedCow::new(SharedContent {
|
||||
name,
|
||||
data,
|
||||
notify_on_drop,
|
||||
})),
|
||||
}),
|
||||
source,
|
||||
}
|
||||
}
|
||||
@ -379,7 +382,8 @@ impl Drop for PluginCustomValue {
|
||||
fn drop(&mut self) {
|
||||
// If the custom value specifies notify_on_drop and this is the last copy, we need to let
|
||||
// the plugin know about it if we can.
|
||||
if self.source.is_some() && self.notify_on_drop() && Arc::strong_count(&self.shared) == 1 {
|
||||
if self.source.is_some() && self.notify_on_drop() && SharedCow::ref_count(&self.shared) == 1
|
||||
{
|
||||
self.get_plugin(None, "drop")
|
||||
// While notifying drop, we don't need a copy of the source
|
||||
.and_then(|plugin| {
|
||||
@ -396,40 +400,3 @@ impl Drop for PluginCustomValue {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A serializable `Arc`, to avoid having to have the serde `rc` feature enabled.
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(transparent)]
|
||||
struct SerdeArc<T>(Arc<T>);
|
||||
|
||||
impl<T> Serialize for SerdeArc<T>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Deserialize<'de> for SerdeArc<T>
|
||||
where
|
||||
T: Deserialize<'de>,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
T::deserialize(deserializer).map(Arc::new).map(SerdeArc)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for SerdeArc<T> {
|
||||
type Target = Arc<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user