From 44846c464f273f0e020fd77e54e30ada7dc2a9cd Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Thu, 17 May 2018 21:18:08 +0200 Subject: [PATCH 1/2] add faster safe rust variant that does not drop needlessly In the split and merge parts, calls to drop were inserted to delete options that had been taken, ie were already none. Thus the calls to drop would always conclude they didn't need to drop, but this would waste computational resources. Using the swap and forget pattern, we can safely inform the compiler that the taken node does not actually need to be freed. --- rust/Cargo.toml | 1 + rust/README.md | 7 ++- rust/src/main.rs | 6 +++ rust/src/swap_forget.rs | 113 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 rust/src/swap_forget.rs diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 9c4c500..7b64a84 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -10,6 +10,7 @@ rand = "0.4.2" refcount = [] idiomatic = [] unsafe = [] +swap_forget = [] [profile.release] lto = true diff --git a/rust/README.md b/rust/README.md index 38fc31c..2a0765f 100644 --- a/rust/README.md +++ b/rust/README.md @@ -4,7 +4,8 @@ Author: Vlad Frolov (@frol) ## Compile -NOTE: The idiomatic, refcount, and unsafe versions of this benchmark are locked behind feature gates. +NOTE: The idiomatic, swap_forget, refcount, and unsafe versions of this +benchmark are locked behind feature gates. ``` cargo build --release --features refcount @@ -18,6 +19,10 @@ strip -s target/release/rust ``` cargo build --release --features unsafe +``` + +``` +cargo build --release --features swap_forget strip -s target/release/rust ``` diff --git a/rust/src/main.rs b/rust/src/main.rs index ea4b469..1f244b7 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -6,6 +6,12 @@ mod idiomatic_impl; #[cfg(feature = "idiomatic")] use idiomatic_impl::Tree; +#[cfg(feature = "swap_forget")] +mod swap_forget; + +#[cfg(feature = "swap_forget")] +use swap_forget::Tree; + #[cfg(feature = "refcount")] mod refcount_impl; diff --git a/rust/src/swap_forget.rs b/rust/src/swap_forget.rs new file mode 100644 index 0000000..79d7e3f --- /dev/null +++ b/rust/src/swap_forget.rs @@ -0,0 +1,113 @@ +use std::mem; + +use rand; + +type NodeCell = Option>; + +struct Node { + x: i32, + y: i32, + left: NodeCell, + right: NodeCell, +} + +impl Node { + fn new(x: i32) -> Self { + Self { + x, + y: rand::random::(), + left: None, + right: None, + } + } +} + +fn merge(lower: NodeCell, greater: NodeCell) -> NodeCell { + match (lower, greater) { + (None, greater) => greater, + + (lower, None) => lower, + + (Some(mut lower_node), Some(mut greater_node)) => { + if lower_node.y < greater_node.y { + let mut merged = merge(lower_node.right.take(), Some(greater_node)); + mem::swap(&mut lower_node.right, &mut merged); + mem::forget(merged); + Some(lower_node) + } else { + let mut merged = merge(Some(lower_node), greater_node.left.take()); + mem::swap(&mut greater_node.left, &mut merged); + mem::forget(merged); + Some(greater_node) + } + } + } +} + +fn split_binary(orig: NodeCell, value: i32) -> (NodeCell, NodeCell) { + if let Some(mut orig_node) = orig { + if orig_node.x < value { + let mut split_pair = split_binary(orig_node.right.take(), value); + ::std::mem::swap(&mut orig_node.right, &mut split_pair.0); + ::std::mem::forget(split_pair.0); + (Some(orig_node), split_pair.1) + } else { + let mut split_pair = split_binary(orig_node.left.take(), value); + ::std::mem::swap(&mut orig_node.left, &mut split_pair.1); + ::std::mem::forget(split_pair.1); + (split_pair.0, Some(orig_node)) + } + } else { + (None, None) + } +} + +fn merge3(lower: NodeCell, equal: NodeCell, greater: NodeCell) -> NodeCell { + merge(merge(lower, equal), greater) +} + +struct SplitResult { + lower: NodeCell, + equal: NodeCell, + greater: NodeCell, +} + +fn split(orig: NodeCell, value: i32) -> SplitResult { + let (lower, equal_greater) = split_binary(orig, value); + let (equal, greater) = split_binary(equal_greater, value + 1); + SplitResult { + lower, + equal, + greater, + } +} + +pub struct Tree { + root: NodeCell, +} + +impl Tree { + pub fn new() -> Self { + Self { root: None } + } + + pub fn has_value(&mut self, x: i32) -> bool { + let splited = split(self.root.take(), x); + let res = splited.equal.is_some(); + self.root = merge3(splited.lower, splited.equal, splited.greater); + res + } + + pub fn insert(&mut self, x: i32) { + let mut splited = split(self.root.take(), x); + if splited.equal.is_none() { + splited.equal = Some(Box::new(Node::new(x))); + } + self.root = merge3(splited.lower, splited.equal, splited.greater); + } + + pub fn erase(&mut self, x: i32) { + let splited = split(self.root.take(), x); + self.root = merge(splited.lower, splited.greater); + } +} From 06a75e8c2d3c97877b4268e28a0462ef2e921726 Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Fri, 18 May 2018 14:49:30 +0200 Subject: [PATCH 2/2] remove unneeded qualification for calls to mem::forget and swap. --- rust/src/swap_forget.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/src/swap_forget.rs b/rust/src/swap_forget.rs index 79d7e3f..7179f1e 100644 --- a/rust/src/swap_forget.rs +++ b/rust/src/swap_forget.rs @@ -48,13 +48,13 @@ fn split_binary(orig: NodeCell, value: i32) -> (NodeCell, NodeCell) { if let Some(mut orig_node) = orig { if orig_node.x < value { let mut split_pair = split_binary(orig_node.right.take(), value); - ::std::mem::swap(&mut orig_node.right, &mut split_pair.0); - ::std::mem::forget(split_pair.0); + mem::swap(&mut orig_node.right, &mut split_pair.0); + mem::forget(split_pair.0); (Some(orig_node), split_pair.1) } else { let mut split_pair = split_binary(orig_node.left.take(), value); - ::std::mem::swap(&mut orig_node.left, &mut split_pair.1); - ::std::mem::forget(split_pair.1); + mem::swap(&mut orig_node.left, &mut split_pair.1); + mem::forget(split_pair.1); (split_pair.0, Some(orig_node)) } } else {