use criterion::{black_box, Criterion};
use flat_spatial::{AABBGrid, Grid};
use rand::{Rng, SeedableRng};
use rstar::{RTree, RTreeObject};
use std::time::{Duration, Instant};
const QUERY_POP: i32 = 500_000;
const SIZE: f32 = 1000.0;
const DENSITY: f32 = (QUERY_POP as f32) / (SIZE * SIZE);
type Data = [f32; 5];
#[derive(Copy, Clone)]
struct AABB {
ll: [f32; 2],
ur: [f32; 2],
}
impl flat_spatial::AABB for AABB {
type V2 = [f32; 2];
fn ll(&self) -> Self::V2 {
self.ll
}
fn ur(&self) -> Self::V2 {
self.ur
}
}
#[derive(Clone)]
struct Rtreedata {
pos: [f32; 2],
data: Data,
}
impl RTreeObject for Rtreedata {
type Envelope = rstar::AABB<[f32; 2]>;
fn envelope(&self) -> Self::Envelope {
rstar::AABB::from_point(self.pos)
}
}
fn query_setup_sparse(s: i32) -> Grid<Data, [f32; 2]> {
let mut grid: Grid<Data, [f32; 2]> = Grid::new(s);
let mut rng = rand::rngs::StdRng::seed_from_u64(1);
(0..QUERY_POP).for_each(|_| {
let r = rng.gen::<[f32; 7]>();
grid.insert([SIZE * r[0], SIZE * r[1]], [r[2], r[3], r[4], r[5], r[6]]);
});
grid
}
fn query_setup_shape(s: i32) -> AABBGrid<Data, AABB> {
let mut grid = AABBGrid::new(s);
let mut rng = rand::rngs::StdRng::seed_from_u64(1);
(0..QUERY_POP).for_each(|_| {
let r = rng.gen::<[f32; 7]>();
let p = [SIZE * r[0], SIZE * r[1]];
grid.insert(AABB { ll: p, ur: p }, [r[2], r[3], r[4], r[5], r[6]]);
});
grid
}
#[inline(never)]
fn query_5_sparsegrid(g: &Grid<Data, [f32; 2]>, iter: u64) -> (Duration, u64) {
let grid = g.clone();
let start = Instant::now();
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
let mut hashres = 0;
for _ in 0..iter {
let pos = [rng.gen::<f32>() * SIZE, rng.gen::<f32>() * SIZE];
grid.query_aabb_visitor(
[pos[0] - 5.0, pos[1] - 5.0],
[pos[0] + 5.0, pos[1] + 5.0],
|x| {
hashres += 1;
black_box(x);
},
);
}
(start.elapsed(), hashres)
}
#[inline(never)]
fn query_5_shapegrid(g: &AABBGrid<Data, AABB>, iter: u64) -> (Duration, u64) {
let grid = g.clone();
let start = Instant::now();
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
let mut hashres = 0;
for _ in 0..iter {
let pos = [rng.gen::<f32>() * SIZE, rng.gen::<f32>() * SIZE];
grid.query_visitor(
AABB {
ll: [pos[0] - 5.0, pos[1] - 5.0],
ur: [pos[0] + 5.0, pos[1] + 5.0],
},
|x, _, _| {
hashres += 1;
black_box(x);
},
)
}
(start.elapsed(), hashres)
}
#[inline(never)]
fn query_5_kdtree(tree: &rstar::RTree<Rtreedata>, iter: u64) -> (Duration, u64) {
let start = Instant::now();
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
let mut hashres = 0;
for _ in 0..iter {
let pos = [rng.gen::<f32>() * SIZE, rng.gen::<f32>() * SIZE];
for x in tree.locate_in_envelope(&rstar::AABB::from_corners(
[pos[0] - 5.0, pos[1] - 5.0],
[pos[0] + 5.0, pos[1] + 5.0],
)) {
hashres += 1;
black_box((x, x.data));
}
}
(start.elapsed(), hashres)
}
#[inline(never)]
fn query_5_kdbush(tree: &kdbush::KDBush, iter: u64) -> (Duration, u64) {
let start = Instant::now();
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
let mut hashres = 0;
for _ in 0..iter {
let pos = [rng.gen::<f32>() * SIZE, rng.gen::<f32>() * SIZE];
tree.range(
(pos[0] - 5.0) as f64,
(pos[1] - 5.0) as f64,
(pos[0] + 5.0) as f64,
(pos[1] + 5.0) as f64,
|x| {
hashres += 1;
black_box(x);
},
)
}
(start.elapsed(), hashres)
}
fn query(c: &mut Criterion) {
let mut c = c.benchmark_group("Query");
let sg5 = query_setup_sparse(5);
let sg10 = query_setup_sparse(10);
let sg20 = query_setup_sparse(20);
let sh5 = query_setup_shape(5);
let sh10 = query_setup_shape(10);
let sh20 = query_setup_shape(20);
let mut tree = RTree::new();
let mut rng = rand::rngs::StdRng::seed_from_u64(0);
(0..QUERY_POP).for_each(|_| {
let r = rng.gen::<[f32; 7]>();
tree.insert(Rtreedata {
pos: [SIZE * r[0], SIZE * r[1]].into(),
data: [r[2], r[3], r[4], r[5], r[6]],
});
});
c.bench_function("query sparseGrid05", |b| {
b.iter_custom(|iter| query_5_sparsegrid(&sg5, iter).0)
});
c.bench_function("query sparseGrid10", |b| {
b.iter_custom(|iter| query_5_sparsegrid(&sg10, iter).0)
});
c.bench_function("query sparseGrid20", |b| {
b.iter_custom(|iter| query_5_sparsegrid(&sg20, iter).0)
});
c.bench_function("query shapeGrid05", |b| {
b.iter_custom(|iter| query_5_shapegrid(&sh5, iter).0)
});
c.bench_function("query shapeGrid10", |b| {
b.iter_custom(|iter| query_5_shapegrid(&sh10, iter).0)
});
c.bench_function("query shapeGrid20", |b| {
b.iter_custom(|iter| query_5_shapegrid(&sh20, iter).0)
});
c.bench_function("query kdtree", |b| {
b.iter_custom(|iter| query_5_kdtree(&tree, black_box(iter)).0)
});
c.finish()
}
fn maintain_sparsegrid(s: i32, iter: u64) -> Duration {
let mut grid: Grid<Data, [f32; 2]> = Grid::new(s);
let mut handles = Vec::with_capacity(iter as usize);
for _ in 0..iter {
let r = rand::random::<[f32; 7]>();
handles.push(grid.insert([SIZE * r[0], SIZE * r[1]], [r[2], r[3], r[4], r[5], r[6]]));
}
let start = Instant::now();
for h in handles {
grid.set_position(h, [rand::random(), rand::random()]);
}
grid.maintain();
start.elapsed()
}
fn maintain_shapegrid(s: i32, iter: u64) -> Duration {
let start = Instant::now();
let mut grid: AABBGrid<Data, AABB> = AABBGrid::new(s);
let mut handles = Vec::with_capacity(iter as usize);
for _ in 0..iter {
let r = rand::random::<[f32; 7]>();
let p = [SIZE * r[0], SIZE * r[1]];
handles.push(grid.insert(AABB { ll: p, ur: p }, [r[2], r[3], r[4], r[5], r[6]]));
}
black_box(grid);
start.elapsed()
}
fn maintain_kdtree_seq(iter: u64) -> Duration {
let start = Instant::now();
let mut tree = RTree::new();
for _ in 0..iter {
let r = rand::random::<[f32; 7]>();
tree.insert(Rtreedata {
pos: [SIZE * r[0], SIZE * r[1]].into(),
data: [r[2], r[3], r[4], r[5], r[6]],
});
}
start.elapsed()
}
fn maintain_kdtree_bulk(iter: u64) -> Duration {
let start = Instant::now();
let v = (0..iter)
.map(|_| {
let r = rand::random::<[f32; 7]>();
Rtreedata {
pos: [SIZE * r[0], SIZE * r[1]].into(),
data: [r[2], r[3], r[4], r[5], r[6]],
}
})
.collect();
let tree = RTree::bulk_load(v);
black_box(tree);
start.elapsed()
}
fn maintain_kdbush_bulk(iter: u64) -> Duration {
let start = Instant::now();
let v: Vec<(f64, f64)> = (0..iter)
.map(|_| {
let r = rand::random::<[f32; 7]>();
((SIZE * r[0]) as f64, (SIZE * r[1]) as f64)
})
.collect();
let tree = kdbush::KDBush::create(v, 10);
black_box(tree);
start.elapsed()
}
fn maintain(c: &mut Criterion) {
let mut g = c.benchmark_group("Maintain");
g.bench_function("maintain sparsegrid5", |b| {
b.iter_custom(|iter| maintain_sparsegrid(black_box(5), iter))
});
g.bench_function("maintain sparsegrid10", |b| {
b.iter_custom(|iter| maintain_sparsegrid(black_box(10), iter))
});
g.bench_function("maintain sparsegrid20", |b| {
b.iter_custom(|iter| maintain_sparsegrid(black_box(20), iter))
});
g.bench_function("maintain shapegrid5", |b| {
b.iter_custom(|iter| maintain_shapegrid(black_box(5), iter))
});
g.bench_function("maintain shapegrid10", |b| {
b.iter_custom(|iter| maintain_shapegrid(black_box(10), iter))
});
g.bench_function("maintain shapegrid20", |b| {
b.iter_custom(|iter| maintain_shapegrid(black_box(20), iter))
});
g.bench_function("maintain kdtree", |b| {
b.iter_custom(|iter| maintain_kdtree_seq(black_box(iter)))
});
g.bench_function("maintain kdtree bulk load", |b| {
b.iter_custom(|iter| maintain_kdtree_bulk(black_box(iter)))
});
g.finish()
}
fn main() {
println!("Density is {}", DENSITY);
let mut pos: Vec<(f64, f64)> = vec![];
let mut rng = rand::rngs::StdRng::seed_from_u64(1);
(0..QUERY_POP).for_each(|_| {
let r = rng.gen::<[f32; 7]>();
pos.push(((SIZE * r[0]) as f64, (SIZE * r[1]) as f64));
});
let tree = kdbush::KDBush::create(pos, 10);
let (t, hash) = query_5_kdbush(&tree, 300_000);
println!(
"query 5 kdbush simple 1M: {}ms hash:{}",
t.as_millis(),
hash
);
let mut tree = RTree::new();
let mut rng = rand::rngs::StdRng::seed_from_u64(1);
(0..QUERY_POP).for_each(|_| {
let r = rng.gen::<[f32; 7]>();
tree.insert(Rtreedata {
pos: [SIZE * r[0], SIZE * r[1]].into(),
data: [r[2], r[3], r[4], r[5], r[6]],
});
});
let (t, hash) = query_5_kdtree(&tree, 300_000);
println!(
"query 5 kdtree simple 1M: {}ms hash:{}",
t.as_millis(),
hash
);
let sg5 = query_setup_sparse(10);
let (t, hash) = query_5_sparsegrid(&sg5, 300_000);
println!(
"query 5 sparse simple 1M: {}ms hash:{}",
t.as_millis(),
hash
);
let sg5 = query_setup_shape(10);
let (t, hash) = query_5_shapegrid(&sg5, 300_000);
println!("query 5 shape simple 1M: {}ms hash:{}", t.as_millis(), hash);
const M: u64 = 5_000_000;
let t = maintain_sparsegrid(10, M);
println!("maintain sparse simple 5M: {}ms", t.as_millis());
let t = maintain_shapegrid(10, M);
println!("maintain shape simple 5M: {}ms", t.as_millis());
let t = maintain_kdtree_bulk(M);
println!("maintain kdtree bulk simple 5M: {}ms", t.as_millis());
let t = maintain_kdbush_bulk(M);
println!("maintain kdbush bulk simple 5M: {}ms", t.as_millis());
}