quality.rs 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940
  1. //! Quality markers for collected recipes: bench-coverage stars and the
  2. //! per-category medal ranks.
  3. use super::render::latest_score;
  4. use super::{ReportEntry, ReportRow};
  5. /// Reports exist but none finished the full scenario pool.
  6. pub(crate) fn incomplete(reports: &[ReportEntry]) -> bool {
  7. !reports.is_empty() && !reports.iter().any(|e| e.done)
  8. }
  9. /// Gold/silver/bronze per (parent model, node count) category, ranked by
  10. /// latest score. Lanes of different node counts don't compete against each
  11. /// other (4x has more GPUs than 2x); recipes without any score don't take
  12. /// a medal. Ties break by recipe name (deterministic).
  13. pub(crate) fn award_medals(rows: &mut [ReportRow]) {
  14. use std::cmp::Ordering;
  15. let mut groups: std::collections::HashMap<(String, u32), Vec<usize>> =
  16. std::collections::HashMap::new();
  17. for (i, r) in rows.iter().enumerate() {
  18. groups
  19. .entry((r.parent.clone(), r.nodes.unwrap_or(1)))
  20. .or_default()
  21. .push(i);
  22. }
  23. for (_, mut idxs) in groups {
  24. idxs.sort_by(|&a, &b| {
  25. let pct = |i: usize| latest_score(&rows[i]).and_then(|e| e.pct);
  26. pct(b)
  27. .partial_cmp(&pct(a))
  28. .unwrap_or(Ordering::Equal)
  29. .then_with(|| rows[a].name.cmp(&rows[b].name))
  30. });
  31. for (rank, &i) in idxs.iter().enumerate().take(3) {
  32. if latest_score(&rows[i]).is_some() {
  33. rows[i].medal = Some(rank as u8 + 1);
  34. }
  35. }
  36. }
  37. }