Skip to content

Commit

Permalink
🐛 Fix FixedVec and perf degradation
Browse files Browse the repository at this point in the history
  • Loading branch information
wrenger committed Dec 20, 2023
1 parent 528da2e commit c9ffffb
Show file tree
Hide file tree
Showing 14 changed files with 63 additions and 104 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ Cargo.lock
*~*
/start.zsh

perf.*
flamegraph/
flamegraph.svg

# Python
venv/
__pycache__/
Expand Down
13 changes: 7 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ license = "MIT"
default-run = "server"

[dependencies]
tokio = { version = "1.20", features = ["full"] }
tokio = { version = "1.35", features = ["full"] }
warp = { version ="0.3", default_features = false }
rand = { version = "0.8", default_features = false, features = ["small_rng", "getrandom", "alloc"] }
serde = { version = "1.0", features = ["derive"] }
Expand All @@ -27,6 +27,12 @@ env_logger = { version = "0.10", default_features = false }
[dev-dependencies]
criterion = { version = "0.5", features = ["async_tokio"] }

[profile.release]
lto = "thin"
opt-level = 3
# strip = true
debug = true

[[bench]]
name = "benchmarks"
harness = false
Expand All @@ -49,8 +55,3 @@ name = "simulate"
harness = false
test = false
bench = false

[profile.release]
lto = "thin"
opt-level = 3
strip = true
2 changes: 1 addition & 1 deletion src/agents/mobility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl MobilityAgent {
let mut grid = game.grid.clone();
for snake in &game.snakes[1..] {
if snake.body.len() >= you.body.len() {
for d in Direction::iter() {
for d in Direction::all() {
let p = snake.head().apply(d);
if grid.has(p) {
grid[p].t = CellT::Owned;
Expand Down
4 changes: 2 additions & 2 deletions src/bin/move.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ use clap::Parser;
#[clap(version, author, about = "Simulate a move for an agent.")]
struct Opts {
/// Default configuration.
#[clap(long, default_value_t, value_parser)]
#[clap(long, default_value_t)]
config: Agent,
/// JSON Game request.
#[clap(value_parser = parse_request)]
request: GameRequest,
/// Time in ms that is subtracted from the game timeouts.
#[clap(long, default_value_t = 200, value_parser)]
#[clap(long, default_value_t = 200)]
latency: usize,
}

Expand Down
19 changes: 10 additions & 9 deletions src/bin/simulate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,34 @@ use std::time::Instant;
#[clap(version, author, about = "Simulate a game between agents.")]
struct Opts {
/// Time each snake has for a turn.
#[clap(long, default_value_t = 200, value_parser)]
#[clap(long, default_value_t = 200)]
timeout: u64,
/// Board height.
#[clap(long, default_value_t = 11, value_parser)]
#[clap(long, default_value_t = 11)]
width: usize,
/// Board width.
#[clap(long, default_value_t = 11, value_parser)]
#[clap(long, default_value_t = 11)]
height: usize,
/// Chance new food spawns.
#[clap(long, default_value_t = 0.15, value_parser)]
#[clap(long, default_value_t = 0.15)]
food_rate: f64,
/// Number of turns after which the hazard expands.
#[clap(short, long, default_value_t = 25, value_parser)]
#[clap(short, long, default_value_t = 25)]
shrink_turns: usize,
/// Number of games that are played.
#[clap(short, long, default_value_t = 1, value_parser)]
#[clap(short, long, default_value_t = 1)]
game_count: usize,
/// Swap agent positions to get more accurate results.
#[clap(long, value_parser)]
#[clap(long)]
swap: bool,
/// Seed for the random number generator.
#[clap(long, default_value_t = 0, value_parser)]
#[clap(long, default_value_t = 0)]
seed: u64,
/// Start config.
#[clap(long, value_parser = parse_request)]
init: Option<GameRequest>,
/// Configurations.
#[clap(value_parser)]
#[clap()]
agents: Vec<Agent>,
}

Expand Down Expand Up @@ -117,6 +117,7 @@ async fn main() {
agents.rotate_left(1);
}

println!("Agents: {agents:?}");
println!("Result: {wins:?}");
}

Expand Down
13 changes: 3 additions & 10 deletions src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,12 @@ impl Neg for Vec2D {
/// The Direction is returned as part of a `MoveResponse`.
///
/// The Y-Axis is positive in the up direction, and X-Axis is positive to the right.
#[derive(Serialize, Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[derive(Serialize, Debug, Default, Clone, Copy, Hash, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
#[repr(u8)]
pub enum Direction {
/// Positive Y
#[default]
Up,
/// Positive X
Right,
Expand All @@ -121,10 +122,8 @@ pub enum Direction {
}

impl Direction {
pub fn iter() -> impl Iterator<Item = Self> {
pub fn all() -> [Self; 4] {
[Self::Up, Self::Right, Self::Down, Self::Left]
.iter()
.copied()
}

/// Returns the invert direction (eg. Left for Right)
Expand All @@ -138,12 +137,6 @@ impl Direction {
}
}

impl Default for Direction {
fn default() -> Self {
Self::Up
}
}

impl From<Vec2D> for Direction {
fn from(p: Vec2D) -> Self {
if p.x < 0 {
Expand Down
45 changes: 12 additions & 33 deletions src/floodfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl FloodFill {

/// Returns if `p` is within the boundaries of the board.
pub fn has(&self, p: Vec2D) -> bool {
0 <= p.x && p.x < self.width as _ && 0 <= p.y && p.y < self.height as _
p.within(self.width, self.height)
}

/// Counts the total health of you or the enemies.
Expand All @@ -120,31 +120,9 @@ impl FloodFill {
.count()
}

/// Counts the space of you or the enemies weighted by the weight function.
pub fn count_space_weighted<F: FnMut(Vec2D, FCell) -> f64>(
&self,
id: u8,
mut weight: F,
) -> f64 {
self.cells
.iter()
.copied()
.enumerate()
.map(|(i, c)| match c {
FCell::Owned { id: o_id, .. } if o_id == id => weight(
Vec2D::new((i % self.width) as i16, (i / self.width) as i16),
c,
),
_ => 0.0,
})
.sum()
}

/// Clears the board so that it can be reused for another floodfill computation.
pub fn clear(&mut self) {
for c in &mut self.cells {
*c = FCell::Free;
}
self.cells.fill(FCell::Free);
}

/// Flood fill combined with ignoring tails depending on distance to head.
Expand Down Expand Up @@ -206,10 +184,12 @@ impl FloodFill {
health,
}) = queue.pop_front()
{
for p in Direction::iter()
.map(|d| p.apply(d))
.filter(|&p| grid.has(p))
{
for d in Direction::all() {
let p = p.apply(d);
if !self.has(p) {
continue;
}

let g_cell = grid[p];
let cell = self[p];

Expand All @@ -218,7 +198,8 @@ impl FloodFill {
let health = if is_food {
100
} else {
health.saturating_sub(if g_cell.hazard { HAZARD_DAMAGE } else { 1 })
let cost = if g_cell.hazard { HAZARD_DAMAGE } else { 1 };
health.saturating_sub(cost)
};

// Collect food
Expand Down Expand Up @@ -275,16 +256,14 @@ impl Index<Vec2D> for FloodFill {
type Output = FCell;

fn index(&self, p: Vec2D) -> &Self::Output {
assert!(0 <= p.x && p.x < self.width as _);
assert!(0 <= p.y && p.y < self.height as _);
debug_assert!(p.within(self.width, self.height));
&self.cells[p.x as usize % self.width + p.y as usize * self.width]
}
}

impl IndexMut<Vec2D> for FloodFill {
fn index_mut(&mut self, p: Vec2D) -> &mut Self::Output {
assert!(0 <= p.x && p.x < self.width as _);
assert!(0 <= p.y && p.y < self.height as _);
debug_assert!(p.within(self.width, self.height));
&mut self.cells[p.x as usize % self.width + p.y as usize * self.width]
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ impl Game {
let mut p = Vec2D::new((p % width) as _, (p / width) as _);
let mut body = VecDeque::new();
body.push_front(p);
while let Some(next) = Direction::iter().find_map(|d| {
while let Some(next) = Direction::all().into_iter().find_map(|d| {
let next = p.apply(d);
(next.within(width, height)
&& raw_cells[(next.x + next.y * width as i16) as usize]
Expand Down
10 changes: 4 additions & 6 deletions src/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ impl Grid {
return Some(make_path(&data, target));
}

for d in Direction::iter() {
for d in Direction::all() {
let neighbor = front.apply(d);
let mut neighbor_cost = cost + 1.0;
if self.is_hazardous(neighbor) {
Expand All @@ -170,7 +170,7 @@ impl Grid {
}

if self.has(neighbor) && self[neighbor].t != CellT::Owned {
let cost_so_far = data.get(&neighbor).map_or(f64::MAX, |(_, c)| *c);
let cost_so_far = data.get(&neighbor).map_or(f64::MAX, |(_, c)| *c);
if neighbor_cost < cost_so_far {
data.insert(neighbor, (front, neighbor_cost));
// queue does not accept float
Expand All @@ -189,16 +189,14 @@ impl Index<Vec2D> for Grid {
type Output = Cell;

fn index(&self, p: Vec2D) -> &Self::Output {
assert!(0 <= p.x && p.x < self.width as _);
assert!(0 <= p.y && p.y < self.height as _);
debug_assert!(p.within(self.width, self.height));
&self.cells[p.x as usize + p.y as usize * self.width]
}
}

impl IndexMut<Vec2D> for Grid {
fn index_mut(&mut self, p: Vec2D) -> &mut Self::Output {
assert!(0 <= p.x && p.x < self.width as _);
assert!(0 <= p.y && p.y < self.height as _);
debug_assert!(p.within(self.width, self.height));
&mut self.cells[p.x as usize + p.y as usize * self.width]
}
}
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ pub mod env;
pub mod floodfill;
pub mod game;
pub mod grid;
mod savegame;
pub mod search;
mod util;

Expand Down
25 changes: 0 additions & 25 deletions src/savegame.rs

This file was deleted.

8 changes: 4 additions & 4 deletions src/search/alphabeta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ async fn async_alphabeta_rec(
let mut value = (Direction::Up, LOSS);

let mut futures = [None, None, None, None];
for d in Direction::iter() {
for d in Direction::all() {
let game = game.clone();
let heuristic = heuristic.clone();
let actions = [d, Direction::Up, Direction::Up, Direction::Up];
Expand All @@ -82,7 +82,7 @@ async fn async_alphabeta_rec(
value
} else {
let mut value = (Direction::Up, WIN);
for d in Direction::iter() {
for d in Direction::all() {
let mut actions = actions;
actions[ply] = d;
let newval = async_alphabeta_rec(
Expand Down Expand Up @@ -152,7 +152,7 @@ fn alphabeta_rec(
}
} else if ply == 0 {
let mut value = (Direction::Up, LOSS);
for d in Direction::iter() {
for d in Direction::all() {
let mut actions = actions;
actions[ply] = d;
let newval = alphabeta_rec(game, actions, depth, ply + 1, alpha, beta, heuristic);
Expand All @@ -169,7 +169,7 @@ fn alphabeta_rec(
value
} else {
let mut value = (Direction::Up, WIN);
for d in Direction::iter() {
for d in Direction::all() {
let mut actions = actions;
actions[ply] = d;
let newval = alphabeta_rec(game, actions, depth, ply + 1, alpha, beta, heuristic);
Expand Down
8 changes: 4 additions & 4 deletions src/search/minimax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async fn async_max_n_rec(

let mut futures = [None, None, None, None];

for d in Direction::iter() {
for d in Direction::all() {
if !game.move_is_valid(0, d) {
continue;
}
Expand All @@ -85,7 +85,7 @@ async fn async_max_n_rec(
} else {
let mut min = 2.0 * WIN;
let mut moved = false;
for d in Direction::iter() {
for d in Direction::all() {
if !game.move_is_valid(ply as u8, d) {
continue;
}
Expand Down Expand Up @@ -158,7 +158,7 @@ fn max_n_rec(
} else if ply == 0 {
// collect all outcomes instead of max
let mut result = [LOSS; 4];
for d in Direction::iter() {
for d in Direction::all() {
if !game.move_is_valid(0, d) {
continue;
}
Expand All @@ -170,7 +170,7 @@ fn max_n_rec(
} else {
let mut min = 2.0 * WIN;
let mut moved = false;
for d in Direction::iter() {
for d in Direction::all() {
if !game.move_is_valid(ply as u8, d) {
continue;
}
Expand Down
Loading

0 comments on commit c9ffffb

Please sign in to comment.