use crate::tune::{
AutotuneKey, AutotuneLogContext, AutotuneLogEvent, PersistentCacheKey, Placement, TuneCache,
TunePlan,
};
use alloc::string::String;
use alloc::vec::Vec;
use core::time::Duration;
use cubecl_environment::records::{Record, RecordEffect, Span};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TuneRecord<K> {
pub table: String,
pub entry: PersistentCacheKey<K>,
pub winner: usize,
pub trials: Vec<Trial>,
pub short_circuit: Option<String>,
pub wall: Duration,
#[serde(rename = "dry_run")]
pub launches_discarded: bool,
pub stored: bool,
#[serde(default)]
pub plan: Vec<PlannedCandidate>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PlannedCandidate {
pub name: String,
pub index: usize,
pub batch: Option<usize>,
pub groups: Vec<Placement>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Trial {
pub name: String,
pub wall: Duration,
}
impl<K> Record for TuneRecord<K> {
const KIND: &'static str = "autotune";
}
#[derive(Debug)]
pub(crate) struct TuneRecording<K> {
open: Option<OpenRecording<K>>,
}
#[derive(Debug)]
struct OpenRecording<K> {
span: Span,
table: String,
entry: PersistentCacheKey<K>,
launches_discarded: bool,
plan: Vec<PlannedCandidate>,
}
pub(crate) struct Answer {
pub winner: usize,
pub stored: bool,
}
impl<K: AutotuneKey> TuneRecording<K> {
pub(crate) fn new(cache: &TuneCache<K>, key: &K, checksum: &str) -> Self {
let open = Span::new().map(|span| OpenRecording {
span,
table: cache.table().into(),
entry: PersistentCacheKey {
key: key.clone(),
checksum: checksum.into(),
},
launches_discarded: crate::execution::ProcessMode::current().stream_mode()
== crate::execution::StreamMode::Discard,
plan: Vec::new(),
});
Self { open }
}
pub(crate) fn is_open(&self) -> bool {
self.open.is_some()
}
pub(crate) fn plan(&mut self, plan: &TunePlan, names: &[&str]) {
if let Some(open) = &mut self.open {
open.plan = plan.account(names);
}
}
pub(crate) fn finish(self, answer: Answer, log_context: Option<&AutotuneLogContext>) {
let Some(open) = self.open else {
return;
};
let Some(wall) = open.span.elapsed() else {
return;
};
let mut trials = Vec::new();
let mut short_circuit = None;
for event in log_context.iter().flat_map(|context| &context.events) {
match event {
AutotuneLogEvent::TuningStep(name, wall) => trials.push(Trial {
name: name.clone(),
wall: *wall,
}),
AutotuneLogEvent::ShortCircuit(name) => short_circuit = Some(name.clone()),
}
}
let record = TuneRecord {
table: open.table,
entry: open.entry,
winner: answer.winner,
trials,
short_circuit,
wall,
launches_discarded: open.launches_discarded,
stored: answer.stored,
plan: open.plan,
};
let effect = if record.stored {
RecordEffect::Changed
} else {
RecordEffect::Observed
};
open.span.close(effect, &record);
}
}