use super::{AutotuneKey, AutotuneOperationSet, Tuner};
use crate::{
channel::ComputeChannel, client::ComputeClient, server::ComputeServer, tune::TuneCacheResult,
};
use core::{fmt::Display, hash::Hash};
use hashbrown::HashMap;
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, string::ToString};
pub struct LocalTuner<AK: AutotuneKey, ID> {
state: spin::RwLock<Option<HashMap<ID, Tuner<AK>>>>,
name: &'static str,
}
#[macro_export]
macro_rules! local_tuner {
($name:expr) => {
LocalTuner::new(concat!(module_path!(), "-", $name));
};
() => {
LocalTuner::new(module_path!());
};
}
pub use local_tuner;
impl<AK: AutotuneKey + 'static, ID: Hash + PartialEq + Eq + Clone + Display> LocalTuner<AK, ID> {
pub const fn new(name: &'static str) -> Self {
Self {
state: spin::RwLock::new(None),
name,
}
}
pub fn clear(&self) {
let mut state = self.state.write();
*state = None;
}
pub fn execute<S, C, Out: Send + 'static>(
&self,
id: &ID,
client: &ComputeClient<S, C>,
autotune_operation_set: Box<dyn AutotuneOperationSet<AK, Out>>,
) -> Out
where
S: ComputeServer + 'static,
C: ComputeChannel<S> + 'static,
{
let key = autotune_operation_set.key();
if let Some(map) = self.state.read().as_ref() {
if let Some(tuner) = map.get(id) {
if let TuneCacheResult::Hit { fastest_index } = tuner.fastest(&key) {
let op = autotune_operation_set.fastest(fastest_index);
return op.execute().expect("Should run when selected by autotune.");
}
}
}
let fastest = {
let mut state = self.state.write();
let map = state.get_or_insert_with(Default::default);
let tuner = map.entry(id.clone()).or_insert_with(move || {
let name = self.name.replace("::", "-");
Tuner::new(&name, &id.to_string())
});
#[allow(unused_mut)]
let mut fastest = tuner.fastest(&key);
#[cfg(autotune_persistent_cache)]
if matches!(fastest, TuneCacheResult::Unchecked) {
let checksum = autotune_operation_set.compute_checksum();
tuner.validate_checksum(&key, &checksum);
fastest = tuner.fastest(&key);
}
fastest
};
match fastest {
TuneCacheResult::Hit { fastest_index } => {
return autotune_operation_set
.fastest(fastest_index)
.execute()
.expect("Should run when selected by autotune.");
}
TuneCacheResult::Miss => {
let state = self.state.read();
let state = state.as_ref().expect("Should be initialized");
let tuner = state.get(id).expect("Should be initialized");
tuner.execute_autotune(autotune_operation_set.as_ref(), client);
}
TuneCacheResult::Pending => {
}
TuneCacheResult::Unchecked => {
panic!("Should have checked the cache already.")
}
};
let fastest = {
let mut state = self.state.write();
let state = state.as_mut().expect("Should be initialized");
let tuner = state.get_mut(id).expect("Should be initialized");
tuner.resolve();
match tuner.fastest(&key) {
TuneCacheResult::Hit { fastest_index } => {
fastest_index
}
TuneCacheResult::Pending => {
0
}
TuneCacheResult::Miss => {
panic!("Should have at least started autotuning");
}
TuneCacheResult::Unchecked => {
panic!("Should have checked the cache.")
}
}
};
autotune_operation_set
.fastest(fastest)
.execute()
.expect("Should run when selected by autotune.")
}
}