From 91911fca1d88079777d5cc53054ff454fd0ec65c Mon Sep 17 00:00:00 2001 From: PlexSheep Date: Thu, 18 Apr 2024 15:29:42 +0200 Subject: [PATCH] async in sync is hard --- src/bench/builtin.rs | 34 +++++++++++++++++++++++----------- src/bench/mod.rs | 15 ++++++--------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/bench/builtin.rs b/src/bench/builtin.rs index 2838e37..184d41b 100644 --- a/src/bench/builtin.rs +++ b/src/bench/builtin.rs @@ -1,4 +1,6 @@ -use std::sync::{Arc, RwLock}; +use std::sync::atomic::AtomicBool; +use std::sync::{Arc, Mutex, RwLock}; +use std::thread::JoinHandle; use libpt::log::info; @@ -9,12 +11,13 @@ use crate::wlist::WordList; use super::{Benchmark, Report}; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct BuiltinBenchmark<'wl, WL: WordList, SL: Solver<'wl, WL>> { solver: SL, builder: GameBuilder<'wl, WL>, report: Arc>, - finished: bool + finished: AtomicBool, + bench_th: Arc>>>> // HACK: this is unholy } impl<'wl, WL, SL> Benchmark<'wl, WL, SL> for BuiltinBenchmark<'wl, WL, SL> @@ -32,42 +35,51 @@ where threads: usize, ) -> crate::error::WResult { info!("using {threads} threads for benchmarking"); - rayon::ThreadPoolBuilder::new() - .num_threads(threads) - .build_global() - .unwrap(); Ok(Self { solver, report: Arc::new(RwLock::new(Report::new(builder.build()?))), builder, - finished: false + finished: AtomicBool::new(false), + bench_th: Arc::new(Mutex::new(None)) }) } + #[inline] fn solver(&self) -> SL { self.solver.clone() } + #[inline] fn builder(&'wl self) -> game::GameBuilder<'wl, WL> { self.builder.clone() } + #[inline] fn solver_ref(&'wl self) -> &'wl SL { &self.solver } + #[inline] fn builder_ref(&'wl self) -> &'wl game::GameBuilder<'wl, WL> { &self.builder } + #[inline] fn report_shared(&'wl self) -> Arc> { self.report.clone() } + #[inline] fn report(&'wl self) -> super::Report { self.report.read().expect("lock is poisoned").clone() } fn is_finished(&self) -> bool { - self.finished + self.finished.load(std::sync::atomic::Ordering::Relaxed) } - fn start(&self) -> WResult<()> { - todo!(); + fn start(&'wl self, n: usize) -> WResult<()> { + let report = self.report_shared(); + let solver = self.solver(); + let builder = self.builder(); + let th = std::thread::spawn(move||{ + Self::bench(n, report, solver, builder) + }); + *self.bench_th.lock().expect("lock is poisoned") = Some(th); Ok(()) } } diff --git a/src/bench/mod.rs b/src/bench/mod.rs index 4e9e8de..f60a430 100644 --- a/src/bench/mod.rs +++ b/src/bench/mod.rs @@ -18,7 +18,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>: Sized + Debug + Sync + Clone +pub trait Benchmark<'wl, WL, SL>: Sized + Debug + Sync where WL: WordList, WL: 'wl, @@ -44,28 +44,25 @@ where // 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 - fn bench(&'wl self, n: usize) -> WResult { - let report = self.report_shared(); - let this = std::sync::Arc::new(self); + fn bench(n: usize, report: Arc>, solver: SL, builder: GameBuilder<'wl, WL>) -> WResult { (0..n) .into_par_iter() .for_each_with(report.clone(), |outside_data, _i| { let report = outside_data; - let r = this - .play() + let r = solver + .play(&mut builder.build().expect("could not create game")) .expect("error playing the game during benchmark"); report.write().expect("lock is poisoned").add(r); }); report.write().expect("lock is poisoned").finalize(); - drop(report); - Ok(self.report()) + Ok(report.read().expect("lock is poisoned").clone()) } // PERF: Somehow returning &Report would be better as we don't need to clone then fn report(&'wl self) -> Report; fn report_shared(&'wl self) -> Arc>; - fn start(&self) -> WResult<()>; + fn start(&'wl self, n: usize) -> WResult<()>; fn is_finished(&self) -> bool; }