diff --git a/src/bench/builtin.rs b/src/bench/builtin.rs index bbe6716..ed0d30c 100644 --- a/src/bench/builtin.rs +++ b/src/bench/builtin.rs @@ -1,19 +1,42 @@ use std::sync::{Arc, Mutex}; +use std::thread::JoinHandle; use libpt::log::info; use crate::error::{BenchError, Error, WResult}; -use crate::game::{Game, GameBuilder}; +use crate::game::{self, Game, GameBuilder}; use crate::solve::Solver; use crate::wlist::WordList; use super::{Benchmark, Report}; -#[derive(Debug, Clone)] +use rayon::prelude::*; + +#[derive(Debug)] pub struct BuiltinBenchmark<'wl, WL: WordList, SL: Solver<'wl, WL>> { solver: SL, builder: GameBuilder<'wl, WL>, report: Arc>, + benchth: Option>, +} +impl<'wl, WL, SL> BuiltinBenchmark<'wl, WL, SL> +where + WL: WordList, + WL: 'wl, + SL: Solver<'wl, WL>, + SL: 'wl, + SL: Send, +{ + #[inline] + fn inner_bench(outside_data: (Arc>, GameBuilder<'wl, WL>, SL), _index: usize) { + todo!() + } + fn run_bench(n: usize, report: Arc>, builder: GameBuilder<'wl, WL>, solver: SL) { + (0..n) + .into_par_iter() + .map(|idx| Self::inner_bench((report.clone(), builder.clone(), solver.clone()), idx)); + report.lock().expect("lock is poisoned").finalize(); + } } impl<'wl, WL, SL> Benchmark<'wl, WL, SL> for BuiltinBenchmark<'wl, WL, SL> @@ -22,25 +45,36 @@ where WL: 'wl, SL: Solver<'wl, WL>, SL: 'wl, + SL: Send, { fn build( - wordlist: &'wl WL, + _wordlist: &'wl WL, solver: SL, builder: GameBuilder<'wl, WL>, - threads: usize + threads: usize, ) -> crate::error::WResult { info!("using {threads} threads for benchmarking"); - rayon::ThreadPoolBuilder::new().num_threads(threads).build_global().unwrap(); + rayon::ThreadPoolBuilder::new() + .num_threads(threads) + .build_global() + .unwrap(); Ok(Self { solver, report: Arc::new(Mutex::new(Report::new(builder.build()?))), builder, + benchth: None, }) } - fn solver(&self) -> &SL { + fn solver(&self) -> SL { + self.solver.clone() + } + fn builder(&'wl self) -> game::GameBuilder<'wl, WL> { + self.builder.clone() + } + fn solver_ref(&'wl self) -> &'wl SL { &self.solver } - fn builder(&'wl self) -> &'wl crate::game::GameBuilder<'wl, WL> { + fn builder_ref(&'wl self) -> &'wl game::GameBuilder<'wl, WL> { &self.builder } @@ -51,4 +85,21 @@ where fn report(&'wl self) -> super::Report { self.report.lock().expect("lock is poisoned").clone() } + + fn start(&'wl mut self, n: usize) -> WResult<()> { + let report = self.report_mutex(); // FIXME: needs to keep self borrowed for some reason? + let solver = self.solver(); + let builder = self.builder(); + let benchth = std::thread::spawn(move || Self::run_bench(n, report, builder, solver)); + + self.benchth = Some(benchth); + Ok(()) + } + + fn is_finished(&self) -> Option { + match &self.benchth { + Some(th) => Some(th.is_finished()), + None => None, + } + } } diff --git a/src/bench/mod.rs b/src/bench/mod.rs index ae0234e..658aa1b 100644 --- a/src/bench/mod.rs +++ b/src/bench/mod.rs @@ -19,7 +19,7 @@ pub mod builtin; /// Default amount of games to play for a [Benchmark] pub const DEFAULT_N: usize = 50; -pub trait Benchmark<'wl, WL, SL>: Clone + Sized + Debug + Sync +pub trait Benchmark<'wl, WL, SL>: Sized + Debug + Sync where WL: WordList, WL: 'wl, @@ -30,18 +30,24 @@ where wordlist: &'wl WL, solver: SL, builder: GameBuilder<'wl, WL>, - threads: usize + threads: usize, ) -> crate::error::WResult; - fn builder(&'wl self) -> &'wl GameBuilder<'wl, WL>; + fn builder(&'wl self) -> GameBuilder<'wl, WL>; + fn builder_ref(&'wl self) -> &'wl GameBuilder<'wl, WL>; fn make_game(&'wl self) -> WResult> { - Ok(self.builder().build()?) + Ok(self.builder_ref().build()?) } - fn solver(&'wl self) -> &'wl SL; + fn solver(&'wl self) -> SL; + fn solver_ref(&'wl self) -> &'wl SL; fn play(&'wl self) -> WResult { - self.solver().play(&mut self.make_game()?) + self.solver_ref().play(&mut self.make_game()?) } + fn start(&'wl mut self, n: usize) -> WResult<()>; + fn is_finished(&self) -> Option; // TODO: add some interface to get reports while the benchmark runs // TODO: make the benchmark optionally multithreaded + // NOTE: This is blocking, use start to let it run in another thread + #[deprecated] fn bench(&'wl self, n: usize) -> WResult { let report = self.report_mutex(); let this = std::sync::Arc::new(self); diff --git a/src/bench/report.rs b/src/bench/report.rs index ad3ad31..c6b0c58 100644 --- a/src/bench/report.rs +++ b/src/bench/report.rs @@ -142,8 +142,12 @@ impl Display for Report { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "n: {}, win_ratio: {:.2}%, avg_score: {:.4} steps until finish, avgerage time per game: {}μs, \n\ - rating: {:.4}, full time until completion: {}ms + "n: {}\n\ + win_ratio: {:.2}%\n\ + avg_score: {:.4}\n\ + avgerage time per game: {}μs\n\ + full time until completion: {}ms\n\ + rating: {:.4}\n\ ", self.n(), self.avg_win() * 100.0, diff --git a/src/bin/bench/cli.rs b/src/bin/bench/cli.rs index e2772cc..0480bd7 100644 --- a/src/bin/bench/cli.rs +++ b/src/bin/bench/cli.rs @@ -11,7 +11,8 @@ use wordle_analyzer::bench::builtin::BuiltinBenchmark; use wordle_analyzer::bench::report::Report; use wordle_analyzer::bench::{Benchmark, DEFAULT_N}; use wordle_analyzer::error::WResult; -use wordle_analyzer::solve::{BuiltinSolverNames, Solver}; +use wordle_analyzer::game::GameBuilder; +use wordle_analyzer::solve::{AnyBuiltinSolver, BuiltinSolverNames, Solver}; use wordle_analyzer::wlist::builtin::BuiltinWList; use wordle_analyzer::{self, game}; @@ -54,24 +55,22 @@ fn main() -> anyhow::Result<()> { trace!("dumping CLI: {:#?}", cli); let wl = BuiltinWList::default(); - let builder = game::Game::builder(&wl) + let builder: GameBuilder<'_, BuiltinWList> = game::Game::builder(&wl) .length(cli.length) .max_steps(cli.max_steps) .precompute(cli.precompute); - let solver = cli.solver.to_solver(&wl); - let bench = Arc::new(BuiltinBenchmark::build(&wl, solver, builder, cli.threads)?); - let bench_running = bench.clone(); + let solver: AnyBuiltinSolver<'_, BuiltinWList> = cli.solver.to_solver(&wl); + let mut bench = BuiltinBenchmark::build(&wl, solver, builder, cli.threads)?; trace!("{bench:#?}"); let n = cli.n; - let bench_th: std::thread::JoinHandle> = - std::thread::spawn(move || bench_running.bench(n)); + bench.start(n)?; - while !bench_th.is_finished() { + while !bench.is_finished()? { println!("{}", bench.report()); } - // finished report - println!("{}", bench_th.join().expect("thread go boom")?); + // FIXME: Rustc thinks wl is borrowed at this point, but it is not!!!!! Or at least it should + // not be Ok(()) }