Skip to content

Commit ddb2d0b

Browse files
authored
Merge pull request #162 from orxfun/parallel-walks
Parallel walks
2 parents 6d3f76a + b42beab commit ddb2d0b

18 files changed

+1552
-91
lines changed

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
[package]
22
name = "orx-tree"
3-
version = "1.6.0"
3+
version = "1.7.0"
44
edition = "2024"
55
authors = ["orxfun <[email protected]>"]
6-
description = "A beautiful tree 🌳 with convenient and efficient growth, mutation and traversal features with support for parallel computation."
6+
description = "A beautiful tree 🌳 with convenient, efficient, parallelizable growth, mutation and features."
77
license = "MIT OR Apache-2.0"
88
repository = "https://github.com/orxfun/orx-tree/"
99
keywords = ["tree", "data-structures", "traversal", "traverse", "binarytree"]
@@ -16,7 +16,7 @@ orx-pinned-vec = "3.16.0"
1616
orx-self-or = "1.2.0"
1717
serde = { version = "1.0.219", optional = true, default-features = false }
1818
orx-split-vec = { version = "3.17.0", default-features = false }
19-
orx-selfref-col = { version = "2.9.0", default-features = false }
19+
orx-selfref-col = { version = "2.10.0", default-features = false }
2020
orx-concurrent-iter = { version = "2.1.0", default-features = false }
2121
orx-parallel = { version = "2.1.0", default-features = false, optional = true }
2222

@@ -34,5 +34,5 @@ default = ["orx-parallel"]
3434
serde = ["dep:serde"]
3535

3636
[[bench]]
37-
name = "parallelization_ref"
37+
name = "walk_iterator"
3838
harness = false

README.md

Lines changed: 84 additions & 51 deletions
Large diffs are not rendered by default.

benches/children_iterator.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
2+
#[cfg(feature = "orx-parallel")]
3+
use orx_parallel::ParIter;
4+
use orx_tree::*;
5+
6+
fn build_tree(n: usize) -> DynTree<String> {
7+
let mut tree = DynTree::new(0.to_string());
8+
let mut dfs = Traversal.dfs().over_nodes();
9+
while tree.len() < n {
10+
let root = tree.root();
11+
let x: Vec<_> = root.leaves_with(&mut dfs).map(|x| x.idx()).collect();
12+
for idx in x.iter() {
13+
let count = tree.len();
14+
let mut node = tree.node_mut(idx);
15+
let num_children = 4;
16+
for j in 0..num_children {
17+
node.push_child((count + j).to_string());
18+
}
19+
}
20+
}
21+
tree
22+
}
23+
24+
fn fibonacci(n: u64) -> u64 {
25+
let mut a = 0;
26+
let mut b = 1;
27+
for _ in 0..n {
28+
let c = a + b;
29+
a = b;
30+
b = c;
31+
}
32+
a
33+
}
34+
35+
fn compute_subtree_value(subtree: &DynNode<String>) -> u64 {
36+
subtree
37+
.walk::<Dfs>()
38+
.map(|x| x.parse::<u64>().unwrap())
39+
.map(|x| fibonacci(x % 1000))
40+
.sum()
41+
}
42+
43+
fn children(tree: &DynTree<String>) -> u64 {
44+
tree.root()
45+
.children()
46+
.map(|x| compute_subtree_value(&x))
47+
.sum()
48+
}
49+
50+
#[cfg(feature = "orx-parallel")]
51+
fn children_par(tree: &DynTree<String>) -> u64 {
52+
tree.root()
53+
.children_par()
54+
.map(|x| compute_subtree_value(&x))
55+
.sum()
56+
}
57+
58+
#[cfg(feature = "orx-parallel")]
59+
fn children_par_2threads(tree: &DynTree<String>) -> u64 {
60+
tree.root()
61+
.children_par()
62+
.num_threads(2)
63+
.map(|x| compute_subtree_value(&x))
64+
.sum()
65+
}
66+
67+
fn bench(c: &mut Criterion) {
68+
let treatments = vec![1_024 * 64];
69+
70+
let mut group = c.benchmark_group("children_iterator");
71+
72+
for n in &treatments {
73+
let tree = build_tree(*n);
74+
75+
let expected = children(&tree);
76+
77+
group.bench_with_input(BenchmarkId::new("NodeRef::children()", n), n, |b, _| {
78+
let result = children(&tree);
79+
assert_eq!(result, expected);
80+
b.iter(|| children(&tree))
81+
});
82+
83+
#[cfg(feature = "orx-parallel")]
84+
group.bench_with_input(
85+
BenchmarkId::new("NNodeRef::children_par()", n),
86+
n,
87+
|b, _| {
88+
let result = children_par(&tree);
89+
assert_eq!(result, expected);
90+
b.iter(|| children_par(&tree))
91+
},
92+
);
93+
94+
#[cfg(feature = "orx-parallel")]
95+
group.bench_with_input(
96+
BenchmarkId::new("NodeRef::children_par().num_threads(2)", n),
97+
n,
98+
|b, _| {
99+
let result = children_par_2threads(&tree);
100+
assert_eq!(result, expected);
101+
b.iter(|| children_par_2threads(&tree))
102+
},
103+
);
104+
}
105+
106+
group.finish();
107+
}
108+
109+
criterion_group!(benches, bench);
110+
criterion_main!(benches);

benches/parallelization_owned.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ fn tree_into_bfs(mut tree: DynTree<String>) -> i64 {
5656
.sum()
5757
}
5858

59+
#[cfg(feature = "orx-parallel")]
5960
fn tree_into_par_x(tree: DynTree<String>) -> i64 {
6061
tree.into_par()
6162
.map(|x| x.parse::<usize>().unwrap())
@@ -107,6 +108,7 @@ fn bench(c: &mut Criterion) {
107108
},
108109
);
109110

111+
#[cfg(feature = "orx-parallel")]
110112
group.bench_with_input(
111113
BenchmarkId::new("Tree::into_par_x() - orx-parallel", n),
112114
n,

benches/parallelization_ref.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ fn tree_bfs(tree: &DynTree<String>) -> i64 {
5656
.sum()
5757
}
5858

59+
#[cfg(feature = "orx-parallel")]
5960
fn tree_par_x(tree: &DynTree<String>) -> i64 {
6061
tree.par()
6162
.map(|x| x.parse::<usize>().unwrap())
@@ -107,6 +108,7 @@ fn bench(c: &mut Criterion) {
107108
},
108109
);
109110

111+
#[cfg(feature = "orx-parallel")]
110112
group.bench_with_input(
111113
BenchmarkId::new("Tree::par_x() - orx-parallel", n),
112114
n,

benches/paths_iterator.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
2+
use orx_iterable::{IntoCloningIterable, Iterable};
3+
#[cfg(feature = "orx-parallel")]
4+
use orx_parallel::ParIter;
5+
use orx_tree::*;
6+
7+
fn build_tree(n: usize) -> DynTree<String> {
8+
let mut tree = DynTree::new(0.to_string());
9+
let mut dfs = Traversal.dfs().over_nodes();
10+
while tree.len() < n {
11+
let root = tree.root();
12+
let x: Vec<_> = root.leaves_with(&mut dfs).map(|x| x.idx()).collect();
13+
for idx in x.iter() {
14+
let count = tree.len();
15+
let mut node = tree.node_mut(idx);
16+
let num_children = 4;
17+
for j in 0..num_children {
18+
node.push_child((count + j).to_string());
19+
}
20+
}
21+
}
22+
tree
23+
}
24+
25+
fn fibonacci(n: i64) -> i64 {
26+
let mut a = 0;
27+
let mut b = 1;
28+
for _ in 0..n {
29+
let c = a + b;
30+
a = b;
31+
b = c;
32+
}
33+
a
34+
}
35+
36+
fn path_value<'a>(path: impl IntoIterator<Item = &'a String>) -> i64 {
37+
path.into_iter()
38+
.map(|x| x.parse::<i64>().unwrap())
39+
.map(|x| x % 500)
40+
.map(fibonacci)
41+
.max()
42+
.unwrap()
43+
}
44+
45+
fn paths<T: Traverser>(tree: &DynTree<String>) -> Vec<String> {
46+
let root = tree.root();
47+
root.paths::<T>()
48+
.map(|x| x.into_iterable())
49+
.max_by_key(|x| path_value(x.iter()))
50+
.map(|x| x.iter().cloned().collect())
51+
.unwrap()
52+
}
53+
54+
#[cfg(feature = "orx-parallel")]
55+
fn paths_par<T: Traverser>(tree: &DynTree<String>) -> Vec<String> {
56+
let root = tree.root();
57+
root.paths_par::<T>()
58+
.map(|x| x.into_iterable())
59+
.max_by_key(|x| path_value(x.iter()))
60+
.map(|x| x.iter().cloned().collect())
61+
.unwrap()
62+
}
63+
64+
type TRAVERSER = Dfs;
65+
66+
fn bench(c: &mut Criterion) {
67+
let treatments = vec![1_024 * 64];
68+
69+
let mut group = c.benchmark_group("paths_iterator");
70+
71+
for n in &treatments {
72+
let data = build_tree(*n);
73+
74+
let expected = paths::<TRAVERSER>(&data);
75+
76+
group.bench_with_input(BenchmarkId::new("NodeRef::paths::<T>()", n), n, |b, _| {
77+
let result = paths::<TRAVERSER>(&data);
78+
assert_eq!(path_value(&result), path_value(&expected));
79+
b.iter(|| paths::<TRAVERSER>(&data))
80+
});
81+
82+
#[cfg(feature = "orx-parallel")]
83+
group.bench_with_input(
84+
BenchmarkId::new("NodeRef::paths_par::<T>()", n),
85+
n,
86+
|b, _| {
87+
let result = paths_par::<TRAVERSER>(&data);
88+
assert_eq!(path_value(&result), path_value(&expected));
89+
b.iter(|| paths_par::<TRAVERSER>(&data))
90+
},
91+
);
92+
}
93+
94+
group.finish();
95+
}
96+
97+
criterion_group!(benches, bench);
98+
criterion_main!(benches);

0 commit comments

Comments
 (0)