use std::cmp::Reverse;
use std::collections::BinaryHeap;
use std::io::BufRead;
use criterion::{black_box, Bencher, Criterion};
use criterion::{criterion_group, criterion_main};
use radix_heap::RadixHeapMap;
type Pos = (u32, u32);
struct Bool2D {
height: u32,
width: u32,
values: Vec<bool>,
}
impl Bool2D {
fn new(height: u32, width: u32) -> Bool2D {
Bool2D {
height,
width,
values: vec![false; (height * width) as usize],
}
}
fn parse_map(mut bytes: &[u8]) -> Bool2D {
let mut line = String::new();
bytes.read_line(&mut line).unwrap();
assert_eq!(line, "type octile\n");
line.clear();
bytes.read_line(&mut line).unwrap();
let mut words = line.split_whitespace();
assert_eq!(words.next(), Some("height"));
let height: u32 = words.next().unwrap().parse().unwrap();
line.clear();
bytes.read_line(&mut line).unwrap();
let mut words = line.split_whitespace();
assert_eq!(words.next(), Some("width"));
let width: u32 = words.next().unwrap().parse().unwrap();
line.clear();
bytes.read_line(&mut line).unwrap();
assert_eq!(line, "map\n");
line.clear();
let mut values = Vec::new();
for _ in 0..height {
bytes.read_line(&mut line).unwrap();
values.extend(line.as_bytes().iter().map(|&x| x == b'.'));
line.clear();
}
Bool2D {
height,
width,
values,
}
}
fn clear(&mut self) {
self.values.fill(false);
}
fn get_mut(&mut self, pos: Pos) -> &mut bool {
&mut self.values[(pos.0 * self.height + pos.1) as usize]
}
}
fn astar<H: AStarHeap>(b: &mut Bencher) {
let from = (40, 75);
let to = (20, 10);
let expected_distance = 85;
let map = Bool2D::parse_map(include_bytes!("den203d.map"));
let mut visited = Bool2D::new(map.height, map.width);
let manhattan =
|pos: Pos| (pos.0.max(to.0) - pos.0.min(to.0)) + (pos.1.max(to.1) - pos.1.min(to.1));
let mut heap = H::new();
b.iter(|| {
heap.clear();
visited.clear();
heap.push(AStarEntry {
pos: from,
cost: 0,
full_cost: manhattan(from),
});
loop {
if let Some(AStarEntry { pos, cost, .. }) = heap.pop() {
if pos == to {
assert_eq!(black_box(cost), expected_distance);
break;
}
for neighbor in [
(pos.0 + 1, pos.1),
(pos.0, pos.1 + 1),
(pos.0, pos.1 - 1),
(pos.0 - 1, pos.1),
] {
let visited = visited.get_mut(neighbor);
if !*visited {
let neighbor_cost = cost + 1;
heap.push(AStarEntry {
pos: neighbor,
cost: neighbor_cost,
full_cost: neighbor_cost + manhattan(neighbor),
});
*visited = true;
}
}
} else {
panic!("no path")
}
}
});
}
struct AStarEntry {
pos: Pos,
cost: u32,
full_cost: u32,
}
impl PartialOrd for AStarEntry {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AStarEntry {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
other
.full_cost
.cmp(&self.full_cost)
.then_with(|| self.cost.cmp(&other.cost))
}
}
impl PartialEq for AStarEntry {
fn eq(&self, other: &Self) -> bool {
self.full_cost == other.full_cost && self.cost == other.cost
}
}
impl Eq for AStarEntry {}
trait AStarHeap {
fn new() -> Self;
fn clear(&mut self);
fn push(&mut self, entry: AStarEntry);
fn pop(&mut self) -> Option<AStarEntry>;
}
impl AStarHeap for RadixHeapMap<Reverse<u32>, (Pos, u32)> {
#[inline]
fn new() -> Self {
RadixHeapMap::new()
}
#[inline]
fn clear(&mut self) {
self.clear()
}
#[inline]
fn push(&mut self, entry: AStarEntry) {
self.push(Reverse(entry.full_cost), (entry.pos, entry.cost))
}
#[inline]
fn pop(&mut self) -> Option<AStarEntry> {
self.pop()
.map(|(Reverse(full_cost), (pos, cost))| AStarEntry {
pos,
cost,
full_cost,
})
}
}
impl AStarHeap for BinaryHeap<AStarEntry> {
#[inline]
fn new() -> Self {
BinaryHeap::new()
}
#[inline]
fn clear(&mut self) {
self.clear();
}
#[inline]
fn push(&mut self, entry: AStarEntry) {
self.push(entry)
}
#[inline]
fn pop(&mut self) -> Option<AStarEntry> {
self.pop()
}
}
fn pushpop_radix(b: &mut Bencher) {
let mut heap = RadixHeapMap::<i32, ()>::new();
b.iter(|| {
heap.push(0, ());
for _ in 0..10000 {
let (n, _) = heap.pop().unwrap();
for i in 0..4 {
heap.push(n - i, ());
}
}
heap.clear();
});
}
fn pushpop_binary(b: &mut Bencher) {
let mut heap = BinaryHeap::<i32>::new();
b.iter(|| {
heap.push(0);
for _ in 0..10000 {
let n = heap.pop().unwrap();
for i in 0..4 {
heap.push(n - i);
}
}
heap.clear();
});
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function(
"astar_radix",
astar::<RadixHeapMap<Reverse<u32>, (Pos, u32)>>,
);
c.bench_function("astar_binary", astar::<BinaryHeap<AStarEntry>>);
c.bench_function("pushpop_radix", pushpop_radix);
c.bench_function("pushpop_binary", pushpop_binary);
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);