#![feature(custom_test_frameworks)] #![test_runner(criterion::runner)] use criterion::{black_box, Bencher, Criterion}; use criterion_macro::criterion; use hyper_scripter::{ fuzzy::*, my_env_logger, script::ScriptName, util::main_util::prepare_pre_run, }; use rand::{rngs::StdRng, seq::index::sample, Rng, SeedableRng}; #[allow(dead_code)] #[path = "../tests/tool.rs"] mod tool; use tool::*; const LONG: usize = 20; const SHORT: std::ops::Range = 5..15; fn gen_name(rng: &mut StdRng) -> String { const CHARSET: &[u8] = b"///_ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz\ 0123456789"; loop { let s: String = (0..LONG) .map(|_| { let idx = rng.gen_range(0..CHARSET.len()); CHARSET[idx] as char }) .collect(); if s.starts_with("/") || s.ends_with("/") { continue; } if s.find("//").is_some() { continue; } return s; } } fn sample_name(rng: &mut StdRng, name: &str) -> String { loop { let mut ret = "".to_owned(); let len = rng.gen_range(SHORT); let mut idx_sample: Vec<_> = sample(rng, LONG, len).iter().collect(); idx_sample.sort(); for idx in idx_sample.into_iter() { ret.push(name.chars().nth(idx).unwrap()); } if ScriptName::valid(&ret, true, true, true).is_err() { continue; } return ret; } } #[criterion] fn bench_fuzz(c: &mut Criterion) { let _ = my_env_logger::try_init(); let mut rng = StdRng::seed_from_u64(42); const CASE_COUNT: usize = 999; let mut names = vec![]; let mut shorts = vec![]; for _ in 0..CASE_COUNT { let name = gen_name(&mut rng); let short = sample_name(&mut rng, &name); names.push(name); shorts.push(short); } let rt = tokio::runtime::Runtime::new().unwrap(); c.bench_function("fuzzy_func", |b| { b.iter(|| { rt.block_on(async { for short in shorts.iter() { let res = fuzz(short, names.iter(), "/").await.unwrap(); black_box(res); } }); }); }); } struct TetData { data: Vec<(String, [i8; 3])>, } impl TetData { fn new(count: usize, rng: &mut StdRng) -> Self { let mut data = vec![]; for _ in 0..count { let name = gen_name(rng); data.push((name, gen_tag_arr(rng, 0, 1))); } TetData { data } } } fn gen_tag_arr(rng: &mut StdRng, min: i8, max: i8) -> [i8; 3] { let mut tags = [0; 3]; for j in 0..3 { tags[j] = rng.gen_range(min..=max); } tags } fn gen_tag_string(a: &[i8; 3]) -> String { let mut v = vec![]; for (i, &u) in a.iter().enumerate() { match u { 1 => v.push(format!("tag{}", i)), -1 => v.push(format!("^tag{}", i)), _ => (), } } if v.is_empty() { "all".to_owned() } else { v.join(",") } } fn gen_tag_select_string(rng: &mut StdRng, mut a: [i8; 3]) -> String { for i in 0..3 { let should_messup = rng.gen_bool(0.5); if should_messup { a[i] = rng.gen_range(-1..=1); } } gen_tag_string(&a) } struct MyBencher<'a, 'b> { b: &'a mut Bencher<'b>, script_count: usize, epoch: usize, with_alias: bool, check_res: bool, script_content: Option, } struct MyBencherWithSetup<'a, 'b, S> { b: MyBencher<'a, 'b>, setup_epoch: usize, setup: S, } impl<'a, 'b> MyBencher<'a, 'b> { fn new(b: &'a mut Bencher<'b>, script_count: usize, epoch: usize, with_alias: bool) -> Self { MyBencher { b, script_count, epoch, with_alias, check_res: false, script_content: None, } } fn check_res(&mut self) { self.check_res = true; } fn script_content(&mut self, content: &str) { self.script_content = Some(content.to_owned()); } fn with_setup String>( self, setup_epoch: usize, setup: S, ) -> MyBencherWithSetup<'a, 'b, S> { MyBencherWithSetup { b: self, setup_epoch, setup, } } fn run(self, gen_arg: F) where F: FnMut(&mut StdRng, &str, &[i8; 3]) -> String, { self.with_setup(0, |_, _| unreachable!()).run(gen_arg) } } impl<'a, 'b, S: FnMut(&mut StdRng, &str) -> String> MyBencherWithSetup<'a, 'b, S> { /// {script_count} scripts, with random tags from [tag0, tag1, tag2] (2^3 posible combinations) /// Run {epoch} times with cmd argument generated by {gen_arg} fn run(self, mut gen_arg: F) where F: FnMut(&mut StdRng, &str, &[i8; 3]) -> String, { let MyBencherWithSetup { b, setup_epoch, setup: mut bench_setup, } = self; let alias = if b.with_alias { "" } else { "--no-alias" }; let mut rng = StdRng::seed_from_u64(42); let data = TetData::new(b.script_count, &mut rng); let period = 20; // NOTE: 每過一定週期切換標籤篩選器 let args: Vec<_> = (0..b.epoch) .into_iter() .map(|i| { if i % period == 0 { let tag_num = (i / period) % 3; return format!("tags +tag{}", tag_num); } let i = rng.gen_range(0..b.script_count); let data = &data.data[i]; format!("{} {}", alias, gen_arg(&mut rng, &data.0, &data.1)) }) .collect(); let (script_count, check_res, script_content) = (b.script_count, b.check_res, b.script_content); let script_content = script_content.as_deref().unwrap_or("echo $NAME"); b.b.iter_with_setup( || { let _ = setup(); prepare_pre_run(Some("#!/usr/bin/bash")).unwrap(); for (name, tag_arr) in data.data.iter() { let tag_str = gen_tag_string(tag_arr); run!( "--no-alias edit --fast --no-template -t {} {} | {}", tag_str, name, script_content ) .unwrap(); } for _ in 0..setup_epoch { let i = rng.gen_range(0..script_count); let data = &data.data[i]; run!("{}", (bench_setup)(&mut rng, &data.0)).unwrap(); } }, |_| { for arg in args.iter() { let res = run!("{}", arg); if check_res { res.expect("check result!"); } } }, ); } } fn run_criterion(c: &mut Criterion, name: &str, script_count: usize, epoch: usize) { let with_alias = name.contains("alias"); if name.contains("fuzzy") { c.bench_function(name, |b| { let b = MyBencher::new(b, script_count, epoch, with_alias); b.run(|rng, name, tag_arr| { let name = sample_name(rng, name); let select = gen_tag_select_string(rng, tag_arr.clone()); format!("-s +{} {}", select, name) }); }); } else if name.contains("exact") { c.bench_function(name, |b| { let b = MyBencher::new(b, script_count, epoch, with_alias); b.run(|rng, name, tag_arr| { let select = gen_tag_select_string(rng, tag_arr.clone()); format!("-s +{} ={}", select, name) }); }); } else if name.contains("prev") { c.bench_function(name, |b| { let b = MyBencher::new(b, script_count, epoch, with_alias); b.run(|rng, _, _| { let select = gen_tag_string(&gen_tag_arr(rng, -1, 1)); let prev = rng.gen_range(1..=script_count); format!("-s +{} ^{}", select, prev) }); }); } else if name.contains("ls") { c.bench_function(name, |b| { let b = MyBencher::new(b, script_count, epoch, with_alias); b.run(|rng, _, tag_arr| { let select = gen_tag_select_string(rng, tag_arr.clone()); format!("-s +{} ls", select) }); }); } else if name.contains("history") { c.bench_function(name, |b| { let mut b = MyBencher::new(b, script_count, epoch, with_alias); b.check_res(); let b = b.with_setup(script_count * 10, |rng, name| { let i = rng.gen_range(0..5); format!("--no-alias --dummy ={}! {}", name, i) }); b.run(|_, name, _| format!("history show ={}! --with-name", name)); }); } else if name.contains("rmid") { c.bench_function(name, |b| { let mut b = MyBencher::new(b, script_count, epoch, with_alias); b.script_content("$HS_EXE -H $HS_HOME history rm-id $HS_RUN_ID"); b.check_res(); b.run(|_, name, _| format!("={}!", name)); }); } else { panic!("看不懂 benchmark 的名字 {}", name); } } #[criterion] fn bench_massive_fuzzy(c: &mut Criterion) { // run with random tag, with random fuzzy name run_criterion(c, "massive_fuzzy", 200, 400); } #[criterion] fn bench_massive_exact(c: &mut Criterion) { // run with random tag, with random exact name run_criterion(c, "massive_exact", 200, 400); } #[criterion] fn bench_massive_prev(c: &mut Criterion) { run_criterion(c, "massive_prev", 200, 400); } #[criterion] fn bench_massive_ls(c: &mut Criterion) { run_criterion(c, "massive_ls", 200, 400); } #[criterion] fn bench_massive_history(c: &mut Criterion) { // run random history show with exact name and bang! run_criterion(c, "massive_history", 200, 400); } #[criterion] fn bench_massive_rmid(c: &mut Criterion) { run_criterion(c, "massive_rmid", 200, 400); } #[criterion] fn bench_small_fuzzy(c: &mut Criterion) { // run with random tag, with random fuzzy name run_criterion(c, "small_fuzzy", 40, 80); } #[criterion] fn bench_small_exact(c: &mut Criterion) { // run with random tag, with random exact name run_criterion(c, "small_exact", 40, 80); } #[criterion] fn bench_small_prev(c: &mut Criterion) { run_criterion(c, "small_prev", 40, 80); } #[criterion] fn bench_small_ls(c: &mut Criterion) { run_criterion(c, "small_ls", 40, 80); } #[criterion] fn bench_small_history(c: &mut Criterion) { // run random history show with exact name and bang! run_criterion(c, "small_history", 40, 80); } #[criterion] fn bench_small_rmid(c: &mut Criterion) { run_criterion(c, "small_rmid", 40, 80); } #[criterion] fn bench_small_exact_alias(c: &mut Criterion) { // run with random tag, with random exact name run_criterion(c, "small_exact_alias", 40, 80); }