Skip to main content

secs_types/
compute.rs

1//! Application-local compute selection and actual-run provenance.
2use serde::{Deserialize, Serialize};
3use ts_rs::TS;
4
5#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, TS)]
6#[serde(rename_all = "snake_case")]
7#[ts(export)]
8pub enum ComputePreference {
9    /// Prefer a capable, supported discrete GPU; otherwise use the CPU.
10    #[default]
11    Auto,
12    Cpu,
13    Gpu,
14}
15
16#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, TS)]
17#[serde(rename_all = "snake_case")]
18#[ts(export)]
19pub enum ComputeBackend {
20    #[default]
21    Cpu,
22    Cuda,
23}
24
25#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, TS)]
26#[serde(rename_all = "snake_case")]
27#[ts(export)]
28pub enum ComputeReason {
29    Recommended,
30    UserCpu,
31    UserGpu,
32    #[default]
33    NoDevice,
34    RuntimeUnavailable,
35    UnsupportedPlatform,
36    Integrated,
37    Legacy,
38    LowPerformance,
39    InsufficientMemory,
40    DeviceFailure,
41    SmallProblem,
42    /// IIR realization keeps the CPU target because its optimizer amplifies tiny differences.
43    IirReference,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TS)]
47#[ts(export)]
48pub struct GpuDeviceInfo {
49    pub id: u32,
50    pub name: String,
51    pub compute_major: u32,
52    pub compute_minor: u32,
53    pub multiprocessors: u32,
54    #[ts(type = "number")]
55    pub memory_bytes: u64,
56    pub integrated: bool,
57    /// The runtime can execute this device's f64 path.
58    pub supported: bool,
59    /// Hardware suitability for the automatic preference.
60    pub recommended: bool,
61    pub reason: ComputeReason,
62}
63
64#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, TS)]
65#[ts(export)]
66pub struct ComputeStatus {
67    pub preference: ComputePreference,
68    pub selected: ComputeBackend,
69    pub reason: ComputeReason,
70    pub gpu_available: bool,
71    pub device: Option<GpuDeviceInfo>,
72    pub devices: Vec<GpuDeviceInfo>,
73}
74
75#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, TS)]
76#[ts(export)]
77pub struct ComputeReport {
78    pub requested: ComputePreference,
79    /// The backend that actually produced accelerated systems, not a preference.
80    pub backend: ComputeBackend,
81    pub device: Option<String>,
82    /// Identifies the accelerated stage; other correction stages remain on the CPU.
83    pub stage: Option<ComputeStage>,
84    pub accelerated_systems: u32,
85    pub cpu_systems: u32,
86    pub fallbacks: u32,
87    pub reason: Option<ComputeReason>,
88    /// What the accelerator reported for its most severe fallback (the failed
89    /// call and status, for example), at most 200 characters
90    /// (`DETAIL_MAX_CHARS`). Absent without a fallback and in reports written
91    /// before it existed.
92    #[serde(default, skip_serializing_if = "Option::is_none")]
93    #[ts(optional)]
94    pub detail: Option<String>,
95}
96
97impl ComputeReport {
98    /// Upper bound on `detail`, in characters.
99    pub const DETAIL_MAX_CHARS: usize = 200;
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
103#[serde(rename_all = "snake_case")]
104#[ts(export)]
105pub enum ComputeStage {
106    IdealFirSweep,
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn compute_defaults_are_automatic_with_cpu_provenance() {
115        assert_eq!(ComputePreference::default(), ComputePreference::Auto);
116        assert_eq!(ComputeReport::default().backend, ComputeBackend::Cpu);
117        assert!(!ComputeStatus::default().gpu_available);
118    }
119
120    #[test]
121    fn report_detail_is_optional_on_the_wire() {
122        let report = ComputeReport::default();
123        let value = serde_json::to_value(&report).unwrap();
124        assert!(value.get("detail").is_none());
125        assert_eq!(
126            serde_json::from_value::<ComputeReport>(value).unwrap(),
127            report
128        );
129        let failed = ComputeReport {
130            detail: Some("cuMemAlloc: CUDA_ERROR_OUT_OF_MEMORY".into()),
131            ..ComputeReport::default()
132        };
133        let value = serde_json::to_value(&failed).unwrap();
134        assert_eq!(value["detail"], "cuMemAlloc: CUDA_ERROR_OUT_OF_MEMORY");
135        assert_eq!(
136            serde_json::from_value::<ComputeReport>(value).unwrap(),
137            failed
138        );
139    }
140}