// Copyright (c) 2023 Yegor Bugayenko // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // In order to run this single test from the command line: // $ cargo test --test benchmark -- --nocapture use emap::Map; use std::env; use std::time::{Duration, Instant}; const CAP: usize = 10; macro_rules! measure { ($total:expr, $e:expr) => {{ let start = Instant::now(); for _ in 0..$total { std::hint::black_box($e); } start.elapsed() }}; } macro_rules! compare { ($title:expr, $ret:expr, $total:expr, $eV:expr, $eM:expr) => {{ $ret.push(( $title, measure!($total, { $eV(std::hint::black_box(&mut Vec::with_capacity(CAP))) }), measure!($total, { $eM(std::hint::black_box( #[cfg(debug_assertions)] &mut Map::with_capacity_none(CAP), #[cfg(not(debug_assertions))] &mut Map::with_capacity(CAP), )) }), )); }}; } fn benchmark(total: usize) -> Vec<(&'static str, Duration, Duration)> { let mut ret = vec![]; compare!( "i ∈ 0..CAP {M.insert(i, &42); s ∈ M.into_values() {sum += s}}", ret, total, |v: &mut Vec<_>| { let mut sum = 0; for _ in 0..CAP { v.push(&42); for s in v.into_iter() { sum += *s; } } sum }, |v: &mut Map<_>| { let mut sum = 0; for i in 0..CAP { v.insert(i, &42); for s in v.into_values() { sum += *s; } } sum } ); compare!( "i ∈ 0..CAP {M.insert(i, &42); s ∈ M.values() {sum += s}}", ret, total, |v: &mut Vec<_>| { let mut sum = 0; for _ in 0..CAP { v.push(&42); for s in v.iter() { sum += *s; } } sum }, |v: &mut Map<_>| { let mut sum = 0; for i in 0..CAP { v.insert(i, &42); for s in v.values() { sum += *s; } } sum } ); compare!( "i ∈ 0..CAP {M.insert(i, &\"大家好\"); s ∈ M.values() {sum += s.len()}}", ret, total, |v: &mut Vec<_>| { let mut sum = 0; for _ in 0..CAP { v.push(&"大家好"); for s in v.iter() { sum += s.len(); } } sum }, |v: &mut Map<_>| { let mut sum = 0; for i in 0..CAP { v.insert(i, &"大家好"); for s in v.values() { sum += s.len(); } } sum } ); compare!( "i ∈ 0..CAP {M.insert(i, &42); s ∈ M.keys() {sum += s}}", ret, total, |v: &mut Vec<_>| { let mut sum = 0; for _ in 0..CAP { v.push(&42); for s in v.iter() { sum += *s; } } sum }, |v: &mut Map<_>| { let mut sum = 0; for i in 0..CAP { v.insert(i, &42); for k in v.keys() { sum += k; } } sum } ); compare!( "i ∈ 0..CAP {M.insert(i, &42)}; i ∈ CAP-1..0 {M.remove(&i)}", ret, total, |v: &mut Vec<_>| { for _ in 0..CAP { v.push(&42); } for i in CAP - 1..0 { v.remove(i); } }, |v: &mut Map<_>| { for i in 0..CAP { v.insert(i, &42); } for i in CAP - 1..0 { v.remove(i); } } ); compare!( "i ∈ 0..CAP {M.insert(i, &42)}; M.clear(); M.len();", ret, total, |v: &mut Vec<_>| { for _ in 0..CAP { v.push(&42); } v.clear(); v.len() }, |v: &mut Map<_>| { for i in 0..CAP { v.insert(i, &42); } v.clear(); v.len() } ); compare!( "i ∈ 0..CAP {M.insert(i, &\"Hello, world!\")}", ret, total, |v: &mut Vec<_>| { for _ in 0..CAP { v.push(&"Hello, world!"); } }, |v: &mut Map<_>| { for i in 0..CAP { v.insert(i, &"Hello, world!"); } } ); ret } /// Run it from command line: /// /// ```text /// $ cargo test --release benchmark_and_print -- --nocapture /// ``` #[test] pub fn benchmark_and_print() { let times = benchmark(1000); for (m, dv, dm) in times { println!("{m} -> {:.2}x", dv.as_nanos() as f64 / dm.as_nanos() as f64); assert!(dv.as_nanos() > 0); } } pub fn main() { let args: Vec = env::args().collect(); let times = benchmark(args.get(1).unwrap().parse::().unwrap()); let mut lines = vec![]; for (m, dv, dm) in times { lines.push(format!("{m}\t{}\t{}", dv.as_nanos(), dm.as_nanos())); } lines.sort(); for t in lines { println!("{t}"); } }