Skip to content

Commit

Permalink
add faster safe rust variant that does not drop needlessly (#52)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vbarrielle authored and frol committed May 18, 2018
1 parent b9a6ff2 commit c5e9205
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 1 deletion.
1 change: 1 addition & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ rand = "0.4.2"
refcount = []
idiomatic = []
unsafe = []
swap_forget = []

[profile.release]
lto = true
Expand Down
7 changes: 6 additions & 1 deletion rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
```

Expand Down
6 changes: 6 additions & 0 deletions rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
113 changes: 113 additions & 0 deletions rust/src/swap_forget.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use std::mem;

use rand;

type NodeCell = Option<Box<Node>>;

struct Node {
x: i32,
y: i32,
left: NodeCell,
right: NodeCell,
}

impl Node {
fn new(x: i32) -> Self {
Self {
x,
y: rand::random::<i32>(),
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);
}
}

0 comments on commit c5e9205

Please # to comment.