cubecl_monitoring/
utilization.rs1use alloc::string::String;
2use core::fmt;
3
4use cubecl_ir::PciVendor;
5
6#[derive(Clone, Copy, Debug, PartialEq)]
8#[non_exhaustive]
9pub struct DeviceUtilization {
10 pub busy_percent: f32,
12 pub memory_busy_percent: Option<f32>,
16}
17
18impl DeviceUtilization {
19 pub fn new(busy_percent: f32) -> Self {
21 Self {
22 busy_percent: busy_percent.clamp(0.0, 100.0),
23 memory_busy_percent: None,
24 }
25 }
26
27 pub fn with_memory_busy_percent(mut self, memory_busy_percent: f32) -> Self {
29 self.memory_busy_percent = Some(memory_busy_percent.clamp(0.0, 100.0));
30 self
31 }
32}
33
34#[derive(Clone, Copy, Debug, PartialEq)]
36#[non_exhaustive]
37pub struct DeviceTemperature {
38 pub celsius: f32,
40}
41
42impl DeviceTemperature {
43 pub fn new(celsius: f32) -> Self {
45 Self { celsius }
46 }
47}
48
49#[derive(Clone, Copy, Debug, PartialEq)]
51#[non_exhaustive]
52pub struct DevicePower {
53 pub watts: f32,
55}
56
57impl DevicePower {
58 pub fn new(watts: f32) -> Self {
60 Self { watts }
61 }
62}
63
64#[derive(Clone, Debug, PartialEq, Eq)]
67#[non_exhaustive]
68pub enum UtilizationUnavailable {
69 RuntimeNotLinked,
72 RuntimeReadsNoCounter,
74 NoCard,
76 NoCounterForCard(Option<PciVendor>),
79 CardAddressNotReported,
83 DriverLibraryNotFound(String),
86 NoPreviousReading,
89 QueryFailed(String),
92 NoSuchReading,
94}
95
96impl fmt::Display for UtilizationUnavailable {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 match self {
99 Self::RuntimeNotLinked => f.write_str("this build does not link the device's runtime"),
100 Self::RuntimeReadsNoCounter => {
101 f.write_str("the device's runtime reads no utilization counter")
102 }
103 Self::NoCard => f.write_str("the device is no card: a software adapter"),
104 Self::NoCounterForCard(Some(vendor)) => {
105 write!(f, "no counter on this platform for a {vendor} card")
106 }
107 Self::NoCounterForCard(None) => {
108 f.write_str("no counter on this platform for a card of no known vendor")
109 }
110 Self::CardAddressNotReported => {
111 f.write_str("the runtime did not report the address the card's counter is found by")
112 }
113 Self::DriverLibraryNotFound(message) => {
114 write!(f, "the driver's library would not load: {message}")
115 }
116 Self::NoPreviousReading => {
117 f.write_str("the counter measures between two readings, and this was the first")
118 }
119 Self::QueryFailed(message) => write!(f, "the counter gave no reading: {message}"),
120 Self::NoSuchReading => f.write_str("the card's counter keeps no such reading"),
121 }
122 }
123}
124
125impl core::error::Error for UtilizationUnavailable {}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn a_reading_from_0_to_100_is_kept_as_it_is() {
133 for busy_percent in [0.0, 37.5, 100.0] {
134 assert_eq!(
135 DeviceUtilization::new(busy_percent).busy_percent,
136 busy_percent
137 );
138 }
139 }
140
141 #[test]
142 fn the_memory_reading_is_held_to_0_to_100_and_absent_unless_read() {
143 assert_eq!(DeviceUtilization::new(50.0).memory_busy_percent, None);
144 assert_eq!(
145 DeviceUtilization::new(50.0)
146 .with_memory_busy_percent(140.0)
147 .memory_busy_percent,
148 Some(100.0)
149 );
150 }
151
152 #[test]
153 fn a_reading_above_100_is_held_to_100() {
154 for busy_percent in [100.5, f32::MAX] {
155 assert_eq!(DeviceUtilization::new(busy_percent).busy_percent, 100.0);
156 }
157 }
158}