// Copyright 2016 union-find-rs Developers // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. use criterion::{criterion_group, criterion_main, Criterion}; use std::fs::File; use std::io::{BufRead, BufReader}; use union_find::{ QuickFindUf, QuickUnionUf, Union, UnionByRank, UnionByRankSize, UnionBySize, UnionBySizeRank, UnionFind, }; struct Cache<'a, T> { input: &'a Input, init: Option, union1: Option, union2: Option, find1: Option, find2: Option, } impl<'a, T> Cache<'a, T> { fn new(input: &'a Input) -> Cache<'a, T> { Cache { input, init: None, union1: None, union2: None, find1: None, find2: None, } } fn init(&mut self) -> &T where T: UnionFind, V: Union + Default, { if self.init.is_none() { self.init = Some(self.input.init()); } self.init.as_ref().unwrap() } fn union1(&mut self) -> &T where T: UnionFind + Clone, V: Union + Default, { if self.union1.is_none() { let mut uf = self.init().clone(); self.input.union(&mut uf); self.union1 = Some(uf); } self.union1.as_ref().unwrap() } fn union2(&mut self) -> &T where T: UnionFind + Clone, V: Union + Default, { if self.union2.is_none() { let mut uf = self.union1().clone(); self.input.union(&mut uf); self.union2 = Some(uf); } self.union2.as_ref().unwrap() } fn find1(&mut self) -> &T where T: UnionFind + Clone, V: Union + Default, { if self.find1.is_none() { let mut uf = self.union1().clone(); self.input.find_all(&mut uf); self.find1 = Some(uf); } self.find1.as_ref().unwrap() } fn find2(&mut self) -> &T where T: UnionFind + Clone, V: Union + Default, { if self.find2.is_none() { let mut uf = self.union1().clone(); self.input.find_all(&mut uf); self.find2 = Some(uf); } self.find2.as_ref().unwrap() } } #[derive(Clone, Debug)] struct Input { name: &'static str, size: usize, conn: Vec<(usize, usize)>, } impl Input { fn from_file(name: &'static str, file_name: &str) -> Input { let mut reader = BufReader::new(File::open(file_name).unwrap()); let mut buf = String::new(); let _ = reader.read_line(&mut buf).unwrap(); let size = buf.trim().parse::().unwrap(); buf.clear(); let mut conn = vec![]; while reader.read_line(&mut buf).unwrap() > 0 { { let mut sp = buf.split_whitespace(); let a = sp.next().unwrap().parse::().unwrap(); let b = sp.next().unwrap().parse::().unwrap(); conn.push((a, b)); } buf.clear(); } Input { name, size, conn } } fn init(&self) -> T where T: UnionFind, V: Union + Default, { T::new(self.size) } fn union(&self, uf: &mut T) where T: UnionFind, V: Union, { for &(p, q) in &self.conn { uf.union(p, q); } } fn find_all(&self, uf: &mut T) where T: UnionFind, V: Union, { for i in 0..uf.size() { let _ = uf.find(i); } } fn bench_clone_from(&self, c: &mut Criterion, category: &str, cache: &mut Cache) where T: UnionFind + Clone, V: Union + Default, { let id = format!("{}::{}::{}", category, self.name, "clone_from"); let base = cache.init(); let mut uf = base.clone(); c.bench_function(&id, |b| { b.iter(|| { uf.clone_from(base); }) }); } fn bench_union1(&self, c: &mut Criterion, category: &str, cache: &mut Cache) where T: UnionFind + Clone, V: Union + Default, { let id = format!("{}::{}::{}", category, self.name, "union1"); let base = cache.init(); let mut uf = base.clone(); c.bench_function(&id, |b| { b.iter(|| { uf.clone_from(base); self.union(&mut uf); }) }); } fn bench_union2(&self, c: &mut Criterion, category: &str, cache: &mut Cache) where T: UnionFind + Clone, V: Union + Default, { let id = format!("{}::{}::{}", category, self.name, "union2"); let base = cache.union1(); let mut uf = base.clone(); c.bench_function(&id, |b| { b.iter(|| { uf.clone_from(base); self.union(&mut uf); }) }); } fn bench_union3(&self, c: &mut Criterion, category: &str, cache: &mut Cache) where T: UnionFind + Clone, V: Union + Default, { let id = format!("{}::{}::{}", category, self.name, "union3"); let base = cache.union2(); let mut uf = base.clone(); c.bench_function(&id, |b| { b.iter(|| { uf.clone_from(base); self.union(&mut uf); }) }); } fn bench_find1(&self, c: &mut Criterion, category: &str, cache: &mut Cache) where T: UnionFind + Clone, V: Union + Default, { let id = format!("{}::{}::{}", category, self.name, "find1"); let base = cache.union1(); let mut uf = base.clone(); c.bench_function(&id, |b| { b.iter(|| { uf.clone_from(base); self.find_all(&mut uf); }) }); } fn bench_find2(&self, c: &mut Criterion, category: &str, cache: &mut Cache) where T: UnionFind + Clone, V: Union + Default, { let id = format!("{}::{}::{}", category, self.name, "find2"); let base = cache.find1(); let mut uf = base.clone(); c.bench_function(&id, |b| { b.iter(|| { uf.clone_from(base); self.find_all(&mut uf); }) }); } fn bench_find3(&self, c: &mut Criterion, category: &str, cache: &mut Cache) where T: UnionFind + Clone, V: Union + Default, { let id = format!("{}::{}::{}", category, self.name, "find3"); let base = cache.find2(); let mut uf = base.clone(); c.bench_function(&id, |b| { b.iter(|| { uf.clone_from(base); self.find_all(&mut uf); }) }); } fn bench_union(&self, c: &mut Criterion, category: &str) where T: UnionFind + Clone, V: Union + Default, { let mut cache = Cache::::new(self); self.bench_union1(c, category, &mut cache); } fn bench_full(&self, c: &mut Criterion, category: &str) where T: UnionFind + Clone, V: Union + Default, { let mut cache = Cache::::new(self); self.bench_clone_from(c, category, &mut cache); self.bench_union1(c, category, &mut cache); self.bench_union2(c, category, &mut cache); self.bench_union3(c, category, &mut cache); self.bench_find1(c, category, &mut cache); self.bench_find2(c, category, &mut cache); self.bench_find3(c, category, &mut cache); } } fn bench(c: &mut Criterion) { let tiny = Input::from_file("tiny", "etc/tinyUF.txt"); let medium = Input::from_file("medium", "etc/mediumUF.txt"); let large = Input::from_file("large", "etc/largeUF.txt"); for input in &[&tiny, &medium, &large] { input.bench_full::, _>(c, "quick_union"); input.bench_full::, _>(c, "quick_find"); } { let input = &tiny; input.bench_union::, _>(c, "by_size"); input.bench_union::, _>(c, "by_rank"); input.bench_union::, _>(c, "by_size_rank"); input.bench_union::, _>(c, "by_rank_size"); } } criterion_group!(benches, bench); criterion_main!(benches);