From c5e9205d6f6abdc8ebe0d926f510d8ea024de9a4 Mon Sep 17 00:00:00 2001 From: Vincent Barrielle Date: Fri, 18 May 2018 14:52:45 +0200 Subject: [PATCH] add faster safe rust variant that does not drop needlessly (#52) 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..7179f1e --- /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); + 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); + mem::swap(&mut orig_node.left, &mut split_pair.1); + 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); + } +}