Max Känner 2f2b7ac11e
All checks were successful
Build / build (push) Successful in 2m7s
add simulation benchmarks
2025-01-21 19:04:30 +01:00

221 lines
7.0 KiB
Rust

use std::sync::atomic::{AtomicU32, Ordering};
use criterion::{black_box, criterion_group, criterion_main, Bencher, BenchmarkId, Criterion};
use battlesnake::types::{
simulation::Board,
wire::{Battlesnake, Board as WireBoard, Game, Request, RoyaleSettings, Ruleset, Settings},
Coord,
};
fn create_start_snake(coord: Coord) -> Battlesnake {
let id = format!("{coord:?}");
Battlesnake {
id: id.clone(),
name: id.clone(),
health: 100,
body: vec![coord; 3],
latency: "0".into(),
head: coord,
length: 3,
shout: String::new(),
squad: id,
}
}
fn create_standard_start_request(starts: [Coord; 4]) -> Request {
Request {
game: Game {
id: "test".into(),
ruleset: Ruleset {
name: "standard".into(),
version: "0".into(),
settings: Settings {
food_spawn_chance: 15,
minimum_food: 1,
hazard_damage_per_turn: 30,
royale: RoyaleSettings {
shrink_every_n_turns: 20,
},
},
},
map: "standard".into(),
timeout: 500,
source: "other".into(),
},
turn: 0,
board: WireBoard {
height: 11,
width: 11,
food: vec![Coord { x: 5, y: 5 }],
hazards: vec![],
snakes: vec![
create_start_snake(starts[0]),
create_start_snake(starts[1]),
create_start_snake(starts[2]),
create_start_snake(starts[3]),
],
},
you: create_start_snake(starts[0]),
}
}
fn standard(c: &mut Criterion) {
let turns_min = AtomicU32::new(u32::MAX);
let turns_max = AtomicU32::new(u32::MIN);
let turns_sum = AtomicU32::new(0);
let turns_total = AtomicU32::new(0);
let mut group = c.benchmark_group("standard");
group.sample_size(10000);
let benchmark = |b: &mut Bencher, board: &Board| {
b.iter(|| {
let mut board = board.clone();
let turn = board.simulate_random(|board| {
if board.num_snakes() <= 1 {
Some(board.turn())
} else {
None
}
});
if turn < turns_min.load(Ordering::Relaxed) {
turns_min.store(turn, Ordering::Relaxed);
}
if turn > turns_max.load(Ordering::Relaxed) {
turns_max.store(turn, Ordering::Relaxed);
}
turns_sum.fetch_add(turn, Ordering::Relaxed);
turns_total.fetch_add(1, Ordering::Relaxed);
});
};
let request = create_standard_start_request([
Coord { x: 1, y: 1 },
Coord { x: 9, y: 1 },
Coord { x: 1, y: 9 },
Coord { x: 9, y: 9 },
]);
let board = Board::from(&request);
group.bench_with_input(
BenchmarkId::from_parameter("start x"),
black_box(&board),
benchmark,
);
{
let max = turns_max.load(Ordering::Relaxed);
let min = turns_min.load(Ordering::Relaxed);
let sum = turns_sum.load(Ordering::Relaxed);
let total = turns_total.load(Ordering::Relaxed);
let avg = sum / total;
println!("turns: [{min}, {max}] avg {avg} @ {total} samples");
turns_max.store(u32::MIN, Ordering::Relaxed);
turns_min.store(u32::MAX, Ordering::Relaxed);
turns_sum.store(0, Ordering::Relaxed);
turns_total.store(0, Ordering::Relaxed);
}
let request = create_standard_start_request([
Coord { x: 5, y: 1 },
Coord { x: 1, y: 5 },
Coord { x: 5, y: 9 },
Coord { x: 9, y: 5 },
]);
let board = Board::from(&request);
group.bench_with_input(
BenchmarkId::from_parameter("start +"),
black_box(&board),
benchmark,
);
{
let max = turns_max.load(Ordering::Relaxed);
let min = turns_min.load(Ordering::Relaxed);
let sum = turns_sum.load(Ordering::Relaxed);
let total = turns_total.load(Ordering::Relaxed);
let avg = sum / total;
println!("turns: [{min}, {max}] avg {avg} @ {total} samples");
}
}
fn constrictor(c: &mut Criterion) {
let turns_min = AtomicU32::new(u32::MAX);
let turns_max = AtomicU32::new(u32::MIN);
let turns_sum = AtomicU32::new(0);
let turns_total = AtomicU32::new(0);
let mut group = c.benchmark_group("constrictor");
group.sample_size(10000);
let benchmark = |b: &mut Bencher, board: &Board| {
b.iter(|| {
let mut board = board.clone();
let turn = board.simulate_random(|board| {
if board.num_snakes() <= 1 {
Some(board.turn())
} else {
None
}
});
if turn < turns_min.load(Ordering::Relaxed) {
turns_min.store(turn, Ordering::Relaxed);
}
if turn > turns_max.load(Ordering::Relaxed) {
turns_max.store(turn, Ordering::Relaxed);
}
turns_sum.fetch_add(turn, Ordering::Relaxed);
turns_total.fetch_add(1, Ordering::Relaxed);
});
};
let mut request = create_standard_start_request([
Coord { x: 1, y: 1 },
Coord { x: 9, y: 1 },
Coord { x: 1, y: 9 },
Coord { x: 9, y: 9 },
]);
request.game.ruleset.name = "constrictor".into();
let board = Board::from(&request);
group.bench_with_input(
BenchmarkId::from_parameter("start x"),
black_box(&board),
benchmark,
);
{
let max = turns_max.load(Ordering::Relaxed);
let min = turns_min.load(Ordering::Relaxed);
let sum = turns_sum.load(Ordering::Relaxed);
let total = turns_total.load(Ordering::Relaxed);
let avg = sum / total;
println!("turns: [{min}, {max}] avg {avg} @ {total} samples");
turns_max.store(u32::MIN, Ordering::Relaxed);
turns_min.store(u32::MAX, Ordering::Relaxed);
turns_sum.store(0, Ordering::Relaxed);
turns_total.store(0, Ordering::Relaxed);
}
let mut request = create_standard_start_request([
Coord { x: 5, y: 1 },
Coord { x: 1, y: 5 },
Coord { x: 5, y: 9 },
Coord { x: 9, y: 5 },
]);
request.game.ruleset.name = "constrictor".into();
let board = Board::from(&request);
group.bench_with_input(
BenchmarkId::from_parameter("start +"),
black_box(&board),
benchmark,
);
{
let max = turns_max.load(Ordering::Relaxed);
let min = turns_min.load(Ordering::Relaxed);
let sum = turns_sum.load(Ordering::Relaxed);
let total = turns_total.load(Ordering::Relaxed);
let avg = sum / total;
println!("turns: [{min}, {max}] avg {avg} @ {total} samples");
}
}
criterion_group!(benches, standard, constrictor);
criterion_main!(benches);