use core::fmt::Display;
use cubecl::CubeCount;
use crate::Hardware;
use crate::routine::LaunchSettings;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct CandidatePriority(i8);
impl CandidatePriority {
pub const FIRST: Self = Self(64);
pub const LAST: Self = Self(0);
pub fn lowered(self, levels: u32) -> Self {
Self(
i8::try_from(i32::from(self.0) - levels.min(i8::MAX as u32) as i32)
.unwrap_or(Self::LAST.0)
.max(Self::LAST.0),
)
}
pub fn get(self) -> i8 {
self.0
}
}
pub trait TuneCandidate {
type Strategy: Display;
type Key;
type Blueprint: PartialEq;
fn strategy(&self) -> &Self::Strategy;
fn priority(&self, key: &Self::Key) -> CandidatePriority;
fn blueprint(&self, _key: &Self::Key) -> Option<Self::Blueprint> {
None
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Unprioritized<S>(S);
impl<S> Unprioritized<S> {
pub fn of(strategy: S) -> Self {
Self(strategy)
}
}
impl<S: Display> TuneCandidate for Unprioritized<S> {
type Strategy = S;
type Key = ();
type Blueprint = ();
fn strategy(&self) -> &S {
&self.0
}
fn priority(&self, _key: &()) -> CandidatePriority {
CandidatePriority::FIRST
}
}
pub fn priority_of<C: TuneCandidate>(
candidates: &[C],
position: usize,
key: &C::Key,
) -> Option<CandidatePriority> {
let candidate = &candidates[position];
let compiled = candidate.blueprint(key);
let repeats_an_earlier_candidate = compiled.is_some()
&& candidates[..position]
.iter()
.any(|earlier| earlier.blueprint(key) == compiled);
(!repeats_an_earlier_candidate).then(|| candidate.priority(key))
}
pub fn short_of_the_device(hw: &Hardware, settings: &LaunchSettings) -> u32 {
let CubeCount::Static(x, y, z) = settings.cube_count else {
return 0;
};
let cubes = x.saturating_mul(y).saturating_mul(z).max(1);
let Some(wanted) = hw.cubes_to_fill(settings.units_per_cube()) else {
return 0;
};
(wanted / cubes).max(1).ilog2()
}
#[cfg(test)]
mod tests {
use super::*;
struct Candidate {
lowered: u32,
compiles: u8,
}
impl TuneCandidate for Candidate {
type Strategy = u32;
type Key = ();
type Blueprint = u8;
fn strategy(&self) -> &u32 {
&self.lowered
}
fn priority(&self, _key: &()) -> CandidatePriority {
CandidatePriority::FIRST.lowered(self.lowered)
}
fn blueprint(&self, _key: &()) -> Option<u8> {
Some(self.compiles)
}
}
fn candidate(lowered: u32, compiles: u8) -> Candidate {
Candidate { lowered, compiles }
}
#[test]
fn every_distinct_candidate_is_in_the_round() {
let candidates = [candidate(0, 1), candidate(7, 2), candidate(u32::MAX, 3)];
for position in 0..candidates.len() {
assert!(priority_of(&candidates, position, &()).is_some());
}
assert_eq!(
priority_of(&candidates, 2, &()),
Some(CandidatePriority::LAST)
);
}
#[test]
fn a_repeat_of_an_earlier_candidate_is_out_of_the_round() {
let candidates = [candidate(3, 1), candidate(0, 1), candidate(1, 2)];
assert_eq!(
priority_of(&candidates, 0, &()),
Some(CandidatePriority::FIRST.lowered(3))
);
assert_eq!(priority_of(&candidates, 1, &()), None);
assert_eq!(
priority_of(&candidates, 2, &()),
Some(CandidatePriority::FIRST.lowered(1))
);
}
#[test]
fn a_lowered_priority_stays_in_the_round() {
assert_eq!(
CandidatePriority::FIRST.lowered(u32::MAX),
CandidatePriority::LAST
);
assert!(CandidatePriority::LAST.get() >= 0);
}
}