async in sync is hard
cargo devel CI / cargo CI (push) Failing after 1m12s Details

This commit is contained in:
Christoph J. Scherr 2024-04-18 15:29:42 +02:00
parent 0cd46eb2de
commit 91911fca1d
2 changed files with 29 additions and 20 deletions

View File

@ -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; use libpt::log::info;
@ -9,12 +11,13 @@ use crate::wlist::WordList;
use super::{Benchmark, Report}; use super::{Benchmark, Report};
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct BuiltinBenchmark<'wl, WL: WordList, SL: Solver<'wl, WL>> { pub struct BuiltinBenchmark<'wl, WL: WordList, SL: Solver<'wl, WL>> {
solver: SL, solver: SL,
builder: GameBuilder<'wl, WL>, builder: GameBuilder<'wl, WL>,
report: Arc<RwLock<Report>>, report: Arc<RwLock<Report>>,
finished: bool finished: AtomicBool,
bench_th: Arc<Mutex<Option<JoinHandle<WResult<Report>>>>> // HACK: this is unholy
} }
impl<'wl, WL, SL> Benchmark<'wl, WL, SL> for BuiltinBenchmark<'wl, WL, SL> impl<'wl, WL, SL> Benchmark<'wl, WL, SL> for BuiltinBenchmark<'wl, WL, SL>
@ -32,42 +35,51 @@ where
threads: usize, threads: usize,
) -> crate::error::WResult<Self> { ) -> crate::error::WResult<Self> {
info!("using {threads} threads for benchmarking"); info!("using {threads} threads for benchmarking");
rayon::ThreadPoolBuilder::new()
.num_threads(threads)
.build_global()
.unwrap();
Ok(Self { Ok(Self {
solver, solver,
report: Arc::new(RwLock::new(Report::new(builder.build()?))), report: Arc::new(RwLock::new(Report::new(builder.build()?))),
builder, builder,
finished: false finished: AtomicBool::new(false),
bench_th: Arc::new(Mutex::new(None))
}) })
} }
#[inline]
fn solver(&self) -> SL { fn solver(&self) -> SL {
self.solver.clone() self.solver.clone()
} }
#[inline]
fn builder(&'wl self) -> game::GameBuilder<'wl, WL> { fn builder(&'wl self) -> game::GameBuilder<'wl, WL> {
self.builder.clone() self.builder.clone()
} }
#[inline]
fn solver_ref(&'wl self) -> &'wl SL { fn solver_ref(&'wl self) -> &'wl SL {
&self.solver &self.solver
} }
#[inline]
fn builder_ref(&'wl self) -> &'wl game::GameBuilder<'wl, WL> { fn builder_ref(&'wl self) -> &'wl game::GameBuilder<'wl, WL> {
&self.builder &self.builder
} }
#[inline]
fn report_shared(&'wl self) -> Arc<RwLock<Report>> { fn report_shared(&'wl self) -> Arc<RwLock<Report>> {
self.report.clone() self.report.clone()
} }
#[inline]
fn report(&'wl self) -> super::Report { fn report(&'wl self) -> super::Report {
self.report.read().expect("lock is poisoned").clone() self.report.read().expect("lock is poisoned").clone()
} }
fn is_finished(&self) -> bool { fn is_finished(&self) -> bool {
self.finished self.finished.load(std::sync::atomic::Ordering::Relaxed)
} }
fn start(&self) -> WResult<()> { fn start(&'wl self, n: usize) -> WResult<()> {
todo!(); 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(()) Ok(())
} }
} }

View File

@ -18,7 +18,7 @@ pub mod builtin;
/// Default amount of games to play for a [Benchmark] /// Default amount of games to play for a [Benchmark]
pub const DEFAULT_N: usize = 50; 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 where
WL: WordList, WL: WordList,
WL: 'wl, WL: 'wl,
@ -44,28 +44,25 @@ where
// TODO: add some interface to get reports while the benchmark runs // TODO: add some interface to get reports while the benchmark runs
// TODO: make the benchmark optionally multithreaded // TODO: make the benchmark optionally multithreaded
// NOTE: This is blocking, use start to let it run in another thread // NOTE: This is blocking, use start to let it run in another thread
fn bench(&'wl self, n: usize) -> WResult<Report> { fn bench(n: usize, report: Arc<RwLock<Report>>, solver: SL, builder: GameBuilder<'wl, WL>) -> WResult<Report> {
let report = self.report_shared();
let this = std::sync::Arc::new(self);
(0..n) (0..n)
.into_par_iter() .into_par_iter()
.for_each_with(report.clone(), |outside_data, _i| { .for_each_with(report.clone(), |outside_data, _i| {
let report = outside_data; let report = outside_data;
let r = this let r = solver
.play() .play(&mut builder.build().expect("could not create game"))
.expect("error playing the game during benchmark"); .expect("error playing the game during benchmark");
report.write().expect("lock is poisoned").add(r); report.write().expect("lock is poisoned").add(r);
}); });
report.write().expect("lock is poisoned").finalize(); 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 // PERF: Somehow returning &Report would be better as we don't need to clone then
fn report(&'wl self) -> Report; fn report(&'wl self) -> Report;
fn report_shared(&'wl self) -> Arc<RwLock<Report>>; fn report_shared(&'wl self) -> Arc<RwLock<Report>>;
fn start(&self) -> WResult<()>; fn start(&'wl self, n: usize) -> WResult<()>;
fn is_finished(&self) -> bool; fn is_finished(&self) -> bool;
} }