use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PromotionCandidate {
pub binding_slot: u32,
pub access_count: u32,
pub bytes_per_element: u32,
pub distinct_indices_per_workgroup: u32,
pub tile_bytes: u32,
pub estimated_speedup_factor: f32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PromotionPlan {
pub kernel_id: String,
pub candidates: Vec<PromotionCandidate>,
pub total_tile_bytes: u32,
pub budget_bytes: u32,
}
impl PromotionPlan {
#[must_use]
pub fn fits_in_budget(&self) -> bool {
self.total_tile_bytes <= self.budget_bytes
}
pub fn sorted_by_speedup(&self) -> Vec<&PromotionCandidate> {
let mut s: Vec<_> = self.candidates.iter().collect();
s.sort_by(|a, b| {
b.estimated_speedup_factor
.partial_cmp(&a.estimated_speedup_factor)
.unwrap_or(std::cmp::Ordering::Equal)
});
s
}
}
#[cfg(test)]
mod tests {
use super::*;
fn cand(slot: u32, accesses: u32, tile_bytes: u32, speedup: f32) -> PromotionCandidate {
PromotionCandidate {
binding_slot: slot,
access_count: accesses,
bytes_per_element: 4,
distinct_indices_per_workgroup: tile_bytes / 4,
tile_bytes,
estimated_speedup_factor: speedup,
}
}
#[test]
fn empty_plan_fits_in_any_budget() {
let p = PromotionPlan {
kernel_id: "empty".into(),
candidates: vec![],
total_tile_bytes: 0,
budget_bytes: 1024,
};
assert!(p.fits_in_budget());
}
#[test]
fn single_candidate_below_budget_fits() {
let p = PromotionPlan {
kernel_id: "k".into(),
candidates: vec![cand(0, 5, 1024, 7.0)],
total_tile_bytes: 1024,
budget_bytes: 4096,
};
assert!(p.fits_in_budget());
}
#[test]
fn over_budget_does_not_fit() {
let p = PromotionPlan {
kernel_id: "k".into(),
candidates: vec![cand(0, 5, 4096, 7.0), cand(1, 3, 4096, 5.0)],
total_tile_bytes: 8192,
budget_bytes: 4096,
};
assert!(!p.fits_in_budget());
}
#[test]
fn sorted_by_speedup_orders_descending() {
let p = PromotionPlan {
kernel_id: "k".into(),
candidates: vec![
cand(0, 3, 256, 3.0),
cand(1, 7, 256, 11.0),
cand(2, 5, 256, 7.0),
],
total_tile_bytes: 768,
budget_bytes: 4096,
};
let sorted = p.sorted_by_speedup();
assert_eq!(sorted[0].binding_slot, 1);
assert_eq!(sorted[1].binding_slot, 2);
assert_eq!(sorted[2].binding_slot, 0);
}
#[test]
fn empty_sorted_is_empty() {
let p = PromotionPlan {
kernel_id: "k".into(),
candidates: vec![],
total_tile_bytes: 0,
budget_bytes: 4096,
};
assert!(p.sorted_by_speedup().is_empty());
}
}