From 0fea3da31c5313d836fa64b18dfdd8f6f986281f Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Tue, 18 Feb 2025 16:27:04 -0500 Subject: [PATCH] improve benchmarks and marginal perf improvements --- benches/future_children.rs | 17 ++++++++++++----- src/board.rs | 15 ++++++++------- src/future_moves.rs | 24 ++++++++++++------------ 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/benches/future_children.rs b/benches/future_children.rs index 63cb64b..f7a164a 100644 --- a/benches/future_children.rs +++ b/benches/future_children.rs @@ -1,5 +1,5 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use std::hint::black_box; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; +use std::time::Duration; // use crate::future_move::FutureMove; use othello::{board::Board, future_moves::FutureMoves, piece::Piece}; @@ -15,9 +15,16 @@ fn future_move_bench(depth: usize, expire: usize) { } fn criterion_benchmark(c: &mut Criterion) { - c.bench_function("depth 6 expire 4", |b| { - b.iter(|| future_move_bench(black_box(6), black_box(4))) - }); + let mut group = c.benchmark_group("future_move (expire 4)"); + group.measurement_time(Duration::from_secs(60)); + + for (depth, expire) in [(2, 4), (3, 4), (4, 4), (5, 4), (6, 4)].iter() { + group.throughput(Throughput::Elements(*depth as u64)); + group.bench_with_input(BenchmarkId::from_parameter(depth), depth, |b, depth| { + b.iter(|| future_move_bench(*depth, *expire)); + }); + } + group.finish(); } criterion_group!(benches, criterion_benchmark); diff --git a/src/board.rs b/src/board.rs index ec6504d..635377e 100644 --- a/src/board.rs +++ b/src/board.rs @@ -166,7 +166,7 @@ impl Board { } /// Get a reference to a backing [`BitBoard`] - pub fn board(&self, color: Piece) -> &BitBoard { + pub const fn board(&self, color: Piece) -> &BitBoard { match color { Piece::Black => &self.black_board, Piece::White => &self.white_board, @@ -174,7 +174,7 @@ impl Board { } /// Get a mutable reference to a backing [`BitBoard`] - pub fn board_mut(&mut self, color: Piece) -> &mut BitBoard { + pub const fn board_mut(&mut self, color: Piece) -> &mut BitBoard { match color { Piece::Black => &mut self.black_board, Piece::White => &mut self.white_board, @@ -225,11 +225,12 @@ impl Board { } pub fn place(&mut self, i: usize, j: usize, piece: Piece) -> Result<(), &'static str> { - if let Some(what_if_result) = self.what_if(i, j, piece) { - *self = what_if_result; - return Ok(()); - } - Err("move would not propegate") + let what_if_result = self + .what_if(i, j, piece) + .ok_or("move would not propegate")?; + + *self = what_if_result; + return Ok(()); } /// Propegate the board and captures starting from a specific position diff --git a/src/future_moves.rs b/src/future_moves.rs index 8fa36e7..f15b2a2 100644 --- a/src/future_moves.rs +++ b/src/future_moves.rs @@ -152,23 +152,24 @@ impl FutureMoves { parent_idx: usize, lazy_children: bool, ) -> Option> { + let parent = &self.arena[parent_idx]; + // early-exit if a winner for the parent already exists - if self.arena[parent_idx].winner != Winner::None { + if parent.winner != Winner::None { return None; } - let new_color = !self.arena[parent_idx].color; - let parent_lazy = self.arena[parent_idx].lazy_children; + let new_color = !parent.color; let mut new: Vec = // use [`Board::all_positions`] here instead of [`Board::possible_moves`] // because we use [`Board::what_if`] later and we want to reduce calls to [`Board::propegate_from_dry`] Board::all_positions() - .flat_map(|(i, j)| self.arena[parent_idx].board.what_if(i, j, new_color).map(|x| (i, j, x))) + .flat_map(|(i, j)| parent.board.what_if(i, j, new_color).map(move |x| (i, j, x))) .map(|(i, j, new_board)| Move { i, j, board: new_board, - winner: new_board.game_winner(!self.arena[parent_idx].color), + winner: new_board.game_winner(new_color), parent: Some(parent_idx), children: Vec::new(), value: 0, @@ -188,7 +189,7 @@ impl FutureMoves { } let start_idx = self.arena.len(); - if parent_lazy && !lazy_children { + if parent.lazy_children && !lazy_children { self.arena[parent_idx].lazy_children = false; // this move's children are being regenerated after lazy child expiration, don't append first node if new.len() > 1 { @@ -234,15 +235,15 @@ impl FutureMoves { for (depth, nodes) in by_depth.into_iter().enumerate().rev() { for idx in nodes { - let self_value = - self.arena[idx].compute_self_value(self.agent_color) / (depth + 1) as i64; + let node = &self.arena[idx]; + let self_value = node.compute_self_value(self.agent_color) / (depth + 1) as i64; - let children_value = self.arena[idx] + let children_value = node .children .iter() .map(|&child| self.arena[child].value) .sum::() - .checked_div(self.arena[idx].children.len() as i64) + .checked_div(node.children.len() as i64) .unwrap_or(0); self.arena[idx].value = self_value + children_value; @@ -309,8 +310,7 @@ impl FutureMoves { .find(|(_, node)| { node.parent == self.current_root && self.current_root.is_some() - && node.i == i - && node.j == j + && node.coords() == (i, j) }) .map(|x| x.0) .inspect(|&root| self.update_root_idx(root))