use alloc::{format, string::String};
use cubecl_ir::ElemType;
use crate::throughput::ComputeCmmaConfig;
#[derive(Eq, PartialEq, Clone, Hash, Debug, Copy)]
#[cfg_attr(std_io, derive(serde::Serialize, serde::Deserialize))]
pub enum ThroughputMode {
ComputeDirect,
ComputeCmma(ComputeCmmaConfig),
Memory,
}
#[derive(Eq, PartialEq, Clone, Hash, Debug, Copy)]
#[cfg_attr(std_io, derive(serde::Serialize, serde::Deserialize))]
pub struct ThroughputKey {
pub mode: ThroughputMode,
pub dtype: ElemType,
}
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
#[cfg_attr(std_io, derive(serde::Serialize, serde::Deserialize))]
pub struct ThroughputValue {
pub ops_count: usize,
pub duration: core::time::Duration,
}
impl ThroughputValue {
pub const ZERO: Self = Self {
ops_count: 0,
duration: core::time::Duration::ZERO,
};
pub fn ops_per_s(&self) -> f64 {
if self.duration.is_zero() {
return f64::NAN;
}
self.ops_count as f64 / self.duration.as_secs_f64()
}
pub fn bytes_per_s(&self, key: &ThroughputKey) -> f64 {
if self.duration.is_zero() {
return f64::NAN;
}
(self.ops_count * key.dtype.size()) as f64 / self.duration.as_secs_f64()
}
pub fn format(&self, key: &ThroughputKey) -> String {
let unit = match key.mode {
ThroughputMode::ComputeDirect | ThroughputMode::ComputeCmma(_) => "OPS",
ThroughputMode::Memory => "bytes",
};
let mut val_per_s = match key.mode {
ThroughputMode::ComputeDirect | ThroughputMode::ComputeCmma(_) => self.ops_per_s(),
ThroughputMode::Memory => self.bytes_per_s(key),
};
if val_per_s.is_nan() {
return String::from("N/A");
}
let suffixes = ["", "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"];
let mut suffix_idx = 0;
for _ in 0..suffixes.len() - 1 {
if val_per_s < 1000.0 {
break;
}
val_per_s /= 1000.0;
suffix_idx += 1;
}
format!("{val_per_s:.4} {}{unit}/s", suffixes[suffix_idx])
}
}