allocator optimizations
This commit is contained in:
parent
598c38efd6
commit
3d3eb01143
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
|
/flamegraph.svg
|
||||||
|
/perf.data*
|
||||||
|
|||||||
33
src/board.rs
33
src/board.rs
@ -136,6 +136,9 @@ impl Board {
|
|||||||
/// Propegate piece captures originating from (i, j)
|
/// Propegate piece captures originating from (i, j)
|
||||||
/// DO NOT USE THIS ALONE, this should be called as a part of
|
/// DO NOT USE THIS ALONE, this should be called as a part of
|
||||||
/// [`Board::place`] or [`Board::place_and_prop_unchecked`]
|
/// [`Board::place`] or [`Board::place_and_prop_unchecked`]
|
||||||
|
// TODO! this function is responsible for approx 64% of the time spent computing moves in `ComplexAgent`
|
||||||
|
// IDEAS: early-exit from each chain so we don't have to call `diag` (which allocs a lot and uses a lot of cycles)
|
||||||
|
// NOTE! got it down to 24.86% (61.1% decrease) with allocator optimizations
|
||||||
fn propegate_from_dry(&self, i: usize, j: usize, starting_color: Piece) -> Vec<(usize, usize)> {
|
fn propegate_from_dry(&self, i: usize, j: usize, starting_color: Piece) -> Vec<(usize, usize)> {
|
||||||
// Create all chains from the piece being propegated from in `i` and `j` coordinates
|
// Create all chains from the piece being propegated from in `i` and `j` coordinates
|
||||||
let (i_chain, j_chain) = (
|
let (i_chain, j_chain) = (
|
||||||
@ -143,21 +146,16 @@ impl Board {
|
|||||||
split_from(0, BOARD_SIZE - 1, j),
|
split_from(0, BOARD_SIZE - 1, j),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut chains: Vec<Vec<(usize, usize)>> = i_chain
|
let mut chains: Vec<Vec<(usize, usize)>> = Vec::with_capacity(8);
|
||||||
.into_iter()
|
|
||||||
.map(|range| range.into_iter().map(|i| (i, j)).collect())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
chains.extend(
|
chains.extend(i_chain.map(|range| range.into_iter().map(|i| (i, j)).collect()));
|
||||||
j_chain
|
|
||||||
.into_iter()
|
chains.extend(j_chain.map(|range| range.into_iter().map(|j| (i, j)).collect()));
|
||||||
.map(|range| range.into_iter().map(|j| (i, j)).collect()),
|
|
||||||
);
|
|
||||||
|
|
||||||
// handle diagonals
|
// handle diagonals
|
||||||
chains.extend(diag(i, j, 0, 0, BOARD_SIZE - 1, BOARD_SIZE - 1));
|
chains.extend(diag(i, j, 0, 0, BOARD_SIZE - 1, BOARD_SIZE - 1));
|
||||||
|
|
||||||
let mut fill: Vec<(usize, usize)> = Vec::new();
|
let mut fill: Vec<(usize, usize)> = Vec::with_capacity(chains.iter().map(Vec::len).sum());
|
||||||
|
|
||||||
for chain in chains {
|
for chain in chains {
|
||||||
for (chain_length, &(new_i, new_j)) in chain.iter().enumerate() {
|
for (chain_length, &(new_i, new_j)) in chain.iter().enumerate() {
|
||||||
@ -167,15 +165,14 @@ impl Board {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if piece == &starting_color {
|
if piece == &starting_color {
|
||||||
// fill all opposite colors with this color
|
// get history of this chain
|
||||||
let Some(history) = chain.get(..chain_length) else {
|
if let Some(history) = chain.get(..chain_length) {
|
||||||
break;
|
// fill all opposite colors with this color
|
||||||
};
|
for &(i_o, j_o) in history {
|
||||||
|
fill.push((i_o, j_o));
|
||||||
// fill all opposite colors with this color
|
}
|
||||||
for &(i_o, j_o) in history {
|
|
||||||
fill.push((i_o, j_o));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// either the other pieces were replaced, or this was an invalid chain,
|
// either the other pieces were replaced, or this was an invalid chain,
|
||||||
// in both cases, the loop needs to be breaked
|
// in both cases, the loop needs to be breaked
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -58,7 +58,6 @@ struct FutureMoves {
|
|||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
/// Color w.r.t
|
/// Color w.r.t
|
||||||
agent_color: Piece,
|
agent_color: Piece,
|
||||||
gc: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FutureMoves {
|
impl FutureMoves {
|
||||||
@ -69,7 +68,6 @@ impl FutureMoves {
|
|||||||
current_depth: 0,
|
current_depth: 0,
|
||||||
max_depth,
|
max_depth,
|
||||||
agent_color,
|
agent_color,
|
||||||
gc: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,10 +203,7 @@ impl FutureMoves {
|
|||||||
if let Some(root) = new_root {
|
if let Some(root) = new_root {
|
||||||
self.current_root = Some(root);
|
self.current_root = Some(root);
|
||||||
self.current_depth = self.max_depth - self.depth_of(Some(root));
|
self.current_depth = self.max_depth - self.depth_of(Some(root));
|
||||||
self.gc += 1;
|
self.prune_unrelated();
|
||||||
if self.gc > 3 {
|
|
||||||
self.prune_unrelated();
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|||||||
@ -11,8 +11,8 @@ mod piece;
|
|||||||
fn main() {
|
fn main() {
|
||||||
let player1 = complexagent::ComplexAgent::new(Piece::Black);
|
let player1 = complexagent::ComplexAgent::new(Piece::Black);
|
||||||
// let player2 = complexagent::ComplexAgent::new(Piece::White);
|
// let player2 = complexagent::ComplexAgent::new(Piece::White);
|
||||||
let player2 = agent::ManualAgent::new(Piece::White);
|
// let player2 = agent::ManualAgent::new(Piece::White);
|
||||||
// let player2 = agent::RandomAgent::new(Piece::White);
|
let player2 = agent::RandomAgent::new(Piece::White);
|
||||||
let mut game = Game::new(Box::new(player1), Box::new(player2));
|
let mut game = Game::new(Box::new(player1), Box::new(player2));
|
||||||
game.game_loop();
|
game.game_loop();
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/misc.rs
45
src/misc.rs
@ -1,33 +1,30 @@
|
|||||||
use std::{iter::Rev, ops::RangeInclusive};
|
use std::{iter::Rev, ops::RangeInclusive};
|
||||||
|
|
||||||
pub fn split_from<T>(min: T, max: T, x: T) -> Vec<Vec<T>>
|
pub fn split_from<T>(min: T, max: T, x: T) -> [Vec<T>; 2]
|
||||||
where
|
where
|
||||||
T: num::Integer + Copy,
|
T: num::Integer + Copy,
|
||||||
RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
|
RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
|
||||||
Rev<RangeInclusive<T>>: Iterator<Item = T>,
|
Rev<RangeInclusive<T>>: Iterator<Item = T>,
|
||||||
{
|
{
|
||||||
|
let mut output = [const { Vec::new() }; 2];
|
||||||
|
|
||||||
// check that x is in range
|
// check that x is in range
|
||||||
if min > x || x > max {
|
if min > x || x > max {
|
||||||
return Vec::new();
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output: Vec<Vec<T>> = Vec::with_capacity(2);
|
|
||||||
if x > min + T::one() {
|
if x > min + T::one() {
|
||||||
let x_lower = x - T::one();
|
let x_lower = x - T::one();
|
||||||
output.push((min..=x_lower).rev().collect());
|
output[0] = (min..=x_lower).rev().collect();
|
||||||
} else {
|
|
||||||
output.push(Vec::new());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if x + T::one() < max {
|
if x + T::one() < max {
|
||||||
output.push(((x + T::one())..=max).collect());
|
output[1] = ((x + T::one())..=max).collect();
|
||||||
} else {
|
|
||||||
output.push(Vec::new());
|
|
||||||
}
|
}
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diag<T>(i: T, j: T, min_i: T, min_j: T, max_i: T, max_j: T) -> Vec<Vec<(T, T)>>
|
pub fn diag<T>(i: T, j: T, min_i: T, min_j: T, max_i: T, max_j: T) -> [Vec<(T, T)>; 4]
|
||||||
where
|
where
|
||||||
T: num::Integer + Copy,
|
T: num::Integer + Copy,
|
||||||
RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
|
RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
|
||||||
@ -36,25 +33,25 @@ where
|
|||||||
let i_chains = split_from(min_i, max_i, i);
|
let i_chains = split_from(min_i, max_i, i);
|
||||||
let j_chains = split_from(min_j, max_j, j);
|
let j_chains = split_from(min_j, max_j, j);
|
||||||
|
|
||||||
vec![
|
[
|
||||||
i_chains[0]
|
i_chains[0]
|
||||||
.clone()
|
.iter()
|
||||||
.into_iter()
|
.cloned()
|
||||||
.zip(j_chains[0].clone())
|
.zip(j_chains[0].clone())
|
||||||
.collect(),
|
.collect(),
|
||||||
i_chains[1]
|
i_chains[1]
|
||||||
.clone()
|
.iter()
|
||||||
.into_iter()
|
.cloned()
|
||||||
.zip(j_chains[1].clone())
|
.zip(j_chains[1].clone())
|
||||||
.collect(),
|
.collect(),
|
||||||
i_chains[1]
|
i_chains[1]
|
||||||
.clone()
|
.iter()
|
||||||
.into_iter()
|
.cloned()
|
||||||
.zip(j_chains[0].clone())
|
.zip(j_chains[0].clone())
|
||||||
.collect(),
|
.collect(),
|
||||||
i_chains[0]
|
i_chains[0]
|
||||||
.clone()
|
.iter()
|
||||||
.into_iter()
|
.cloned()
|
||||||
.zip(j_chains[1].clone())
|
.zip(j_chains[1].clone())
|
||||||
.collect(),
|
.collect(),
|
||||||
]
|
]
|
||||||
@ -66,16 +63,16 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn split_test() {
|
fn split_test() {
|
||||||
assert_eq!(split_from(0, 6, 2), vec![vec![1, 0], vec![3, 4, 5, 6]]);
|
assert_eq!(split_from(0, 6, 2), [vec![1, 0], vec![3, 4, 5, 6]]);
|
||||||
|
|
||||||
assert_eq!(split_from(0, 6, 0), vec![vec![], vec![1, 2, 3, 4, 5, 6]]);
|
assert_eq!(split_from(0, 6, 0), [vec![], vec![1, 2, 3, 4, 5, 6]]);
|
||||||
|
|
||||||
assert_eq!(split_from(0, 6, 6), vec![vec![5, 4, 3, 2, 1, 0], vec![]]);
|
assert_eq!(split_from(0, 6, 6), [vec![5, 4, 3, 2, 1, 0], vec![]]);
|
||||||
|
|
||||||
// test out-of-bounds and also generics
|
// test out-of-bounds and also generics
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
split_from::<i16>(-1i16, 4i16, 10i16),
|
split_from::<i16>(-1i16, 4i16, 10i16),
|
||||||
Vec::<Vec<i16>>::new()
|
[const { Vec::new() }; 2]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +80,7 @@ mod test {
|
|||||||
fn diag_test() {
|
fn diag_test() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
diag(2, 3, 0, 0, 7, 7),
|
diag(2, 3, 0, 0, 7, 7),
|
||||||
vec![
|
[
|
||||||
vec![(1, 2), (0, 1)],
|
vec![(1, 2), (0, 1)],
|
||||||
vec![(3, 4), (4, 5), (5, 6), (6, 7)],
|
vec![(3, 4), (4, 5), (5, 6), (6, 7)],
|
||||||
vec![(3, 2), (4, 1), (5, 0)],
|
vec![(3, 2), (4, 1), (5, 0)],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user