#![allow(clippy::redundant_closure_call)] extern crate dudy_malloc; #[macro_use] extern crate criterion; use std::{hash::Hash, mem::forget}; use compact_str::CompactString; use criterion::{black_box, BatchSize, BenchmarkId, Criterion}; use rand::distributions::{Alphanumeric, DistString}; use rayon::prelude::*; use rustc_hash::FxHashSet; use smartstring::{LazyCompact, SmartString}; macro_rules! string_creation { ($group:expr, $len:expr, $setup:expr) => {{ let group = &mut $group; for len in $len { group.bench_with_input(BenchmarkId::new("hstr", len), &len, |b, _| { let mut store = hstr::AtomStore::default(); b.iter_batched( $setup(len), |text| { black_box(store.atom(text)); }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("hstr_slow", len), &len, |b, _| { b.iter_batched( $setup(len), |text| { black_box(hstr::Atom::from(text)); }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("string_cache", len), &len, |b, _| { let mut prevent_drop = vec![]; b.iter_batched( $setup(len), |text| { let atom = black_box(string_cache::DefaultAtom::from(text)); prevent_drop.push(atom); }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("compact_str", len), &len, |b, _| { b.iter_batched( $setup(len), |text| { black_box(CompactString::from(text)); }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("smartstring", len), &len, |b, _| { b.iter_batched( $setup(len), |text| { black_box(SmartString::::from(text)); }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("smol_str", len), &len, |b, _| { b.iter_batched( $setup(len), |text| { black_box(smol_str::SmolStr::new(text)); }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("kstring", len), &len, |b, _| { b.iter_batched( $setup(len), |text| { black_box(kstring::KString::from(text)); }, BatchSize::SmallInput, ); }); } }}; } fn bench_basic_creation(c: &mut Criterion) { let length = [4usize, 8, 16, 32, 64, 128, 256, 512, 1024, 2048]; { let mut group = c.benchmark_group("single-thread/create/cached"); string_creation!(group, length, |len| { let text = random_string(len); move || text.clone() }); group.finish(); } { let mut group = c.benchmark_group("single-thread/create/not-cached"); string_creation!(group, length, |len| { move || random_string(len) }); group.finish(); } { let mut group = c.benchmark_group("single-thread/create/mixed"); string_creation!(group, length, |len| { let text = random_string(len); let mut i = 0; move || { i += 1; if black_box(i) % 5 == 0 { text.clone() } else { random_string(len) } } }); group.finish(); } } fn bench_hash_operation(c: &mut Criterion) { fn random_hashset(items: Vec) -> FxHashSet where S: Eq + Hash, { let mut set = FxHashSet::default(); set.extend(items); set } fn random_keys(len: usize, convert: &mut dyn FnMut(String) -> S) -> Vec { (0..len).map(|_| random_string(1024)).map(convert).collect() } fn prepare(len: usize, convert: &mut dyn FnMut(String) -> S) -> (FxHashSet, Vec) where S: Eq + Hash + Clone, { let items = random_keys(len, convert); let set = random_hashset(items.clone()); let mut keys = random_keys(9 * len, &mut *convert); keys.extend(items); (set, keys) } let mut group = c.benchmark_group("single-thread/HashSet"); let length = [1000, 10000]; { for len in length { group.bench_with_input(BenchmarkId::new("hstr", len), &len, |b, _| { let mut for_fairness = vec![]; let mut store = hstr::AtomStore::default(); b.iter_batched( || prepare(len, &mut |s| store.atom(s)), |(map, keys)| { for key in &keys { black_box(map.contains(key)); } for_fairness.extend(map); for_fairness.extend(keys); }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("string_cache", len), &len, |b, _| { let mut for_fairness = vec![]; b.iter_batched( || prepare(len, &mut string_cache::DefaultAtom::from), |(map, keys)| { for key in &keys { black_box(map.contains(key)); } for_fairness.extend(map); for_fairness.extend(keys); }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("compact_str", len), &len, |b, _| { b.iter_batched( || prepare(len, &mut CompactString::from), |(map, keys)| { for key in keys { black_box(map.contains(&key)); } }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("smartstring", len), &len, |b, _| { b.iter_batched( || prepare(len, &mut SmartString::::from), |(map, keys)| { for key in keys { black_box(map.contains(&key)); } }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("smol_str", len), &len, |b, _| { b.iter_batched( || prepare(len, &mut smol_str::SmolStr::from), |(map, keys)| { for key in keys { black_box(map.contains(&key)); } }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("kstring", len), &len, |b, _| { b.iter_batched( || prepare(len, &mut kstring::KString::from), |(map, keys)| { for key in keys { black_box(map.contains(&key)); } }, BatchSize::SmallInput, ); }); } }; group.finish(); } fn bench_parallel_creation(c: &mut Criterion) { { let mut group = c.benchmark_group("parallel/create"); for len in [64, 256, 1024] { group.bench_with_input(BenchmarkId::new("hstr", len), &len, |b, _| { b.iter_batched( || { (0..num_cpus::get()) .map(|_| hstr::AtomStore::default()) .collect::>() }, |stores| { stores .into_par_iter() .map(|mut store| { let atoms = (0..len) .map(|_| store.atom(random_string(65))) .collect::>(); (store, atoms) }) .collect::>() .into_iter() .for_each(|(store, atoms)| { black_box(atoms); forget(store); }); }, BatchSize::SmallInput, ); }); group.bench_with_input(BenchmarkId::new("string_cache", len), &len, |b, _| { b.iter_batched( || {}, |_| { let mut main_store = FxHashSet::default(); (0..num_cpus::get()) .into_par_iter() .map(|_| { (0..len) .map(|_| { black_box(string_cache::DefaultAtom::from(random_string( 65, ))) }) .collect::>() }) .collect::>() .into_iter() .for_each(|v| { main_store.extend(black_box(v)); }); }, BatchSize::SmallInput, ); }); } group.finish(); } } criterion_group!( benches, bench_basic_creation, bench_parallel_creation, bench_hash_operation ); criterion_main!(benches); fn random_string(len: usize) -> String { Alphanumeric.sample_string(&mut rand::thread_rng(), len) }