output bot metrics data in tidepool with "-M" flag

This commit is contained in:
tali 2023-04-16 19:07:16 -04:00
parent 52d339747a
commit 9067459eb1
3 changed files with 65 additions and 24 deletions

View File

@ -42,12 +42,12 @@ pub struct CliArgs {
#[derive(Clone, Debug, Args)] #[derive(Clone, Debug, Args)]
pub struct OutputDataArgs { pub struct OutputDataArgs {
/// Generate profiling data in the output.
#[arg(short = 'P', long)]
pub profile: bool,
/// Generate the full list of moves in the output. /// Generate the full list of moves in the output.
#[arg(short = 'L', long)] #[arg(short = 'L', long)]
pub list_moves: bool, pub list_moves: bool,
/// Generate algorithm metrics in the output.
#[arg(short = 'M', long)]
pub metrics: bool,
} }
#[derive(Clone, Debug, Args)] #[derive(Clone, Debug, Args)]

View File

@ -341,6 +341,7 @@ fn run_simulation(
exit_early: &atomic::AtomicBool, exit_early: &atomic::AtomicBool,
) { ) {
let mut moves = data_args.list_moves.then(Vec::new); let mut moves = data_args.list_moves.then(Vec::new);
let mut metrics = data_args.metrics.then(Vec::new);
let seed = seed.unwrap_or_else(|| rand::thread_rng().next_u64()); let seed = seed.unwrap_or_else(|| rand::thread_rng().next_u64());
let weights = eval::Weights::default(); let weights = eval::Weights::default();
@ -363,6 +364,9 @@ fn run_simulation(
sim.matrix(), sim.matrix(),
mino::Queue::new(sim.queue().hold(), sim.queue().next()), mino::Queue::new(sim.queue().hold(), sim.queue().next()),
); );
if data_args.metrics {
bot.start_instrumenting();
}
while bot.iterations() < config.bot.iters { while bot.iterations() < config.bot.iters {
let gas = std::cmp::min(50_000, config.bot.iters - bot.iterations()); let gas = std::cmp::min(50_000, config.bot.iters - bot.iterations());
@ -385,6 +389,9 @@ fn run_simulation(
if let Some(moves) = moves.as_mut() { if let Some(moves) = moves.as_mut() {
moves.push(output::Move { placement }) moves.push(output::Move { placement })
} }
if let Some(metrics) = metrics.as_mut() {
metrics.push(bot.metrics().unwrap());
}
let status = Status { let status = Status {
lines_left: sim.lines_left(), lines_left: sim.lines_left(),
@ -396,17 +403,13 @@ fn run_simulation(
} }
} }
let profile = data_args.profile.then(|| output::Profile {
time: Instant::now() - time_start,
});
let output = output::Output { let output = output::Output {
seed, seed,
cleared: config.game.goal - sim.lines_left(), cleared: config.game.goal - sim.lines_left(),
pieces: sim.pieces(), pieces: sim.pieces(),
config: config.game, config: config.game,
moves, moves,
profile, metrics,
}; };
if tx.send(Msg::Output(id, output.into())).is_err() { if tx.send(Msg::Output(id, output.into())).is_err() {
tracing::debug!("nobody received output id={id}"); tracing::debug!("nobody received output id={id}");

View File

@ -1,6 +1,6 @@
use fish::bot::Metrics;
use mino::srs::Piece; use mino::srs::Piece;
use serde::Serialize; use serde::Serialize;
use std::time::Duration;
use crate::config::GameConfig; use crate::config::GameConfig;
@ -11,10 +11,13 @@ pub struct Output {
pub config: GameConfig, pub config: GameConfig,
pub pieces: usize, pub pieces: usize,
pub cleared: usize, pub cleared: usize,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub moves: Option<Vec<Move>>, pub moves: Option<Vec<Move>>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(
pub profile: Option<Profile>, serialize_with = "ser::metrics",
skip_serializing_if = "Option::is_none"
)]
pub metrics: Option<Vec<Metrics>>,
} }
impl Output { impl Output {
@ -30,16 +33,6 @@ pub struct Move {
// TODO: spin? // TODO: spin?
} }
#[derive(Serialize, Debug)]
pub struct Profile {
#[serde(serialize_with = "ser::seconds")]
pub time: Duration,
// TODO: pps mean/variance
// TODO: timing stats for specific functions/routines
// TODO: stats info about top <N> evaluations
// TODO: histograms about selected nodes
}
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct SummaryStats<T: Copy> { pub struct SummaryStats<T: Copy> {
count: usize, count: usize,
@ -167,11 +160,56 @@ mod ser {
.serialize(serializer) .serialize(serializer)
} }
pub fn seconds<S>(dur: &Duration, serializer: S) -> Result<S::Ok, S::Error> pub fn metrics<S>(metrics: &Option<Vec<Metrics>>, serializer: S) -> Result<S::Ok, S::Error>
where where
S: serde::Serializer, S: serde::Serializer,
{ {
dur.as_secs_f64().serialize(serializer) #[derive(Serialize)]
struct MoveMetrics {
start: NodeMetrics,
end: NodeMetrics,
}
#[derive(Serialize)]
struct NodeMetrics {
features: Vec<i32>,
heuristic: i32,
#[serde(flatten)]
rating: RatingVariant,
#[serde(skip_serializing_if = "Option::is_none")]
iteration: Option<u32>,
}
#[derive(Serialize)]
#[serde(untagged)]
enum RatingVariant {
NotApplicable,
Rating { rating: i32 },
Solution { solution: i32 },
}
metrics
.iter()
.flat_map(|m| m.iter())
.map(|m| MoveMetrics {
start: NodeMetrics {
features: m.start_features.1.to_vec(),
heuristic: m.start_heuristic,
rating: RatingVariant::NotApplicable,
iteration: None,
},
end: NodeMetrics {
features: m.end_features.1.to_vec(),
heuristic: m.end_heuristic,
rating: match m.end_rating {
(false, v) => RatingVariant::Rating { rating: v },
(true, v) => RatingVariant::Solution { solution: -v },
},
iteration: Some(m.end_iteration),
},
})
.collect::<Vec<_>>()
.serialize(serializer)
} }
pub fn summary_stats<S, T>(stats: &SummaryStats<T>, serializer: S) -> Result<S::Ok, S::Error> pub fn summary_stats<S, T>(stats: &SummaryStats<T>, serializer: S) -> Result<S::Ok, S::Error>