use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum BankConflictKind {
NoConflict,
BroadcastSafe,
Conflict { way_count: u32 },
Unknown,
}
impl std::fmt::Display for BankConflictKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NoConflict => f.write_str("no-conflict"),
Self::BroadcastSafe => f.write_str("broadcast-safe"),
Self::Conflict { way_count } => write!(f, "conflict:{way_count}"),
Self::Unknown => f.write_str("unknown"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ConflictSeverity {
None,
Mild,
Severe,
Critical,
Unknown,
}
impl std::fmt::Display for ConflictSeverity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::None => f.write_str("none"),
Self::Mild => f.write_str("mild"),
Self::Severe => f.write_str("severe"),
Self::Critical => f.write_str("critical"),
Self::Unknown => f.write_str("unknown"),
}
}
}
impl BankConflictKind {
#[must_use]
pub fn severity(&self) -> ConflictSeverity {
match self {
Self::NoConflict | Self::BroadcastSafe => ConflictSeverity::None,
Self::Conflict { way_count } => match *way_count {
0..=1 => ConflictSeverity::None,
2..=4 => ConflictSeverity::Mild,
5..=15 => ConflictSeverity::Severe,
_ => ConflictSeverity::Critical,
},
Self::Unknown => ConflictSeverity::Unknown,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AccessKind {
Load,
Store,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct BankAccessSite {
pub op_index: usize,
pub kind: AccessKind,
pub binding_slot: u32,
pub conflict: BankConflictKind,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct BankConflictReport {
pub kernel_id: String,
pub bank_count: u32,
pub sites: Vec<BankAccessSite>,
}
impl BankConflictReport {
#[must_use]
pub fn problematic_count(&self) -> usize {
self.sites
.iter()
.filter(|s| !matches!(s.conflict.severity(), ConflictSeverity::None))
.count()
}
#[must_use]
pub fn critical_count(&self) -> usize {
self.sites
.iter()
.filter(|s| matches!(s.conflict.severity(), ConflictSeverity::Critical))
.count()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_conflict_severity_is_none() {
assert_eq!(
BankConflictKind::NoConflict.severity(),
ConflictSeverity::None
);
}
#[test]
fn broadcast_safe_severity_is_none() {
assert_eq!(
BankConflictKind::BroadcastSafe.severity(),
ConflictSeverity::None
);
}
#[test]
fn way_2_conflict_is_mild() {
assert_eq!(
BankConflictKind::Conflict { way_count: 2 }.severity(),
ConflictSeverity::Mild
);
}
#[test]
fn way_4_conflict_is_mild() {
assert_eq!(
BankConflictKind::Conflict { way_count: 4 }.severity(),
ConflictSeverity::Mild
);
}
#[test]
fn way_8_conflict_is_severe() {
assert_eq!(
BankConflictKind::Conflict { way_count: 8 }.severity(),
ConflictSeverity::Severe
);
}
#[test]
fn way_15_conflict_is_severe() {
assert_eq!(
BankConflictKind::Conflict { way_count: 15 }.severity(),
ConflictSeverity::Severe
);
}
#[test]
fn way_16_conflict_is_critical() {
assert_eq!(
BankConflictKind::Conflict { way_count: 16 }.severity(),
ConflictSeverity::Critical
);
}
#[test]
fn way_32_conflict_is_critical() {
assert_eq!(
BankConflictKind::Conflict { way_count: 32 }.severity(),
ConflictSeverity::Critical
);
}
#[test]
fn unknown_severity_is_unknown() {
assert_eq!(
BankConflictKind::Unknown.severity(),
ConflictSeverity::Unknown
);
}
#[test]
fn empty_report_has_zero_problematic() {
let r = BankConflictReport {
kernel_id: "empty".into(),
bank_count: 32,
sites: vec![],
};
assert_eq!(r.problematic_count(), 0);
assert_eq!(r.critical_count(), 0);
}
#[test]
fn problematic_count_aggregates_correctly() {
let r = BankConflictReport {
kernel_id: "k".into(),
bank_count: 32,
sites: vec![
BankAccessSite {
op_index: 0,
kind: AccessKind::Load,
binding_slot: 0,
conflict: BankConflictKind::NoConflict,
},
BankAccessSite {
op_index: 1,
kind: AccessKind::Load,
binding_slot: 0,
conflict: BankConflictKind::Conflict { way_count: 4 },
},
BankAccessSite {
op_index: 2,
kind: super::AccessKind::Store,
binding_slot: 1,
conflict: BankConflictKind::Conflict { way_count: 32 },
},
],
};
assert_eq!(r.problematic_count(), 2);
assert_eq!(r.critical_count(), 1);
}
#[test]
fn bank_conflict_kind_formats_stable_strings() {
assert_eq!(format!("{}", BankConflictKind::NoConflict), "no-conflict");
assert_eq!(
format!("{}", BankConflictKind::Conflict { way_count: 16 }),
"conflict:16"
);
assert_eq!(format!("{}", BankConflictKind::Unknown), "unknown");
}
#[test]
fn conflict_severity_formats_stable_strings() {
assert_eq!(format!("{}", ConflictSeverity::None), "none");
assert_eq!(format!("{}", ConflictSeverity::Mild), "mild");
assert_eq!(format!("{}", ConflictSeverity::Severe), "severe");
assert_eq!(format!("{}", ConflictSeverity::Critical), "critical");
assert_eq!(format!("{}", ConflictSeverity::Unknown), "unknown");
}
}