Skip to main content

qualia_core_db/q42/
machine_gpu_profile.rs

1//! Machine GPU capability profile — which **native** tiers beat portable WGSL here.
2//!
3//! WGSL is the universal floor (WebGPU / wgpu). HLSL+DXC, CUDA-C/NVRTC/WMMA, MSL, SPIR-V,
4//! subgroups, and coopmat are higher tiers when present. This profile records what the host
5//! actually has and which decode path was measured best, so install/runtime can prefer
6//! native performance instead of defaulting forever to the WGSL baseline.
7//!
8//! On disk: `machine-gpu-profile.json` (campaign / passport adjacent).
9
10use serde::{Deserialize, Serialize};
11use std::path::{Path, PathBuf};
12
13pub const MACHINE_GPU_PROFILE_VERSION: u32 = 1;
14pub const MACHINE_GPU_PROFILE_NAME: &str = "machine-gpu-profile.json";
15
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
17pub struct ToolchainAvailability {
18    pub wgpu: bool,
19    pub cuda_toolkit: bool,
20    /// DXC CLI for HLSL→SPIR-V/DXIL (`dxc` / QUALIA_DXC_CLI_PATH). Distinct from QUALIA_DXC_PATH (dxcompiler.dll for wgpu).
21    pub dxc_cli: bool,
22    pub metal_xcrun: bool,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
26pub struct AdapterFeatures {
27    pub name: String,
28    pub backend: String,
29    pub discrete: bool,
30    pub subgroups: bool,
31    pub coopmat: bool,
32    pub shader_f16: bool,
33    pub timestamp_query: bool,
34    pub topology_hash: String,
35}
36
37/// One measured decode path (backend + mode + package).
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
39pub struct MeasuredDecodePath {
40    pub wgpu_backend: String,
41    pub inference_mode: String,
42    pub p64_path: String,
43    pub tok_s: f64,
44    pub coherence_ok: bool,
45    pub tokens: u32,
46}
47
48/// Recommended runtime configuration for **this machine**.
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
50pub struct RecommendedPath {
51    /// e.g. `vulkan` | `dx12` | `metal`
52    pub wgpu_backend: String,
53    /// e.g. `fast-verify` | `portable` | `cuda`
54    pub inference_mode: String,
55    /// Why this beats baseline.
56    pub rationale: String,
57    /// Optional QUALIA_* env to apply.
58    pub env: Vec<(String, String)>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
62pub struct MachineGpuProfile {
63    pub version: u32,
64    pub written_unix_ms: u64,
65    pub host: String,
66    pub toolchain: ToolchainAvailability,
67    pub adapter: AdapterFeatures,
68    /// Native tiers that exist on this host (ordered preference for work that can use them).
69    pub native_tiers: Vec<String>,
70    pub measured_paths: Vec<MeasuredDecodePath>,
71    pub recommended: RecommendedPath,
72    pub notes: Vec<String>,
73}
74
75impl MachineGpuProfile {
76    pub fn now_ms() -> u64 {
77        std::time::SystemTime::now()
78            .duration_since(std::time::UNIX_EPOCH)
79            .map(|d| d.as_millis() as u64)
80            .unwrap_or(0)
81    }
82
83    pub fn default_path(out_dir: &Path) -> PathBuf {
84        out_dir.join(MACHINE_GPU_PROFILE_NAME)
85    }
86
87    pub fn write_json(&self, path: &Path) -> Result<(), String> {
88        let json = serde_json::to_string_pretty(self).map_err(|e| format!("serialize: {e}"))?;
89        if let Some(parent) = path.parent() {
90            std::fs::create_dir_all(parent).map_err(|e| format!("mkdir: {e}"))?;
91        }
92        std::fs::write(path, json).map_err(|e| format!("write {}: {e}", path.display()))
93    }
94
95    pub fn load_json(path: &Path) -> Result<Option<Self>, String> {
96        if !path.is_file() {
97            return Ok(None);
98        }
99        let bytes = std::fs::read(path).map_err(|e| format!("read: {e}"))?;
100        let p: Self = serde_json::from_slice(&bytes).map_err(|e| format!("parse: {e}"))?;
101        Ok(Some(p))
102    }
103
104    /// Pick best coherent measured path by tok/s.
105    pub fn recompute_recommended(&mut self) {
106        let best = self
107            .measured_paths
108            .iter()
109            .filter(|p| p.coherence_ok)
110            .max_by(|a, b| {
111                a.tok_s
112                    .partial_cmp(&b.tok_s)
113                    .unwrap_or(std::cmp::Ordering::Equal)
114            });
115        if let Some(b) = best {
116            self.recommended = RecommendedPath {
117                wgpu_backend: b.wgpu_backend.clone(),
118                inference_mode: b.inference_mode.clone(),
119                rationale: format!(
120                    "highest coherent decode-proxy {:.2} tok/s on {} + {} (package {})",
121                    b.tok_s, b.wgpu_backend, b.inference_mode, b.p64_path
122                ),
123                env: vec![
124                    ("QUALIA_WGPU_BACKEND".into(), b.wgpu_backend.clone()),
125                    ("QUALIA_INFERENCE_MODE".into(), b.inference_mode.clone()),
126                ],
127            };
128        }
129    }
130
131    pub fn apply_env_script_ps1(&self) -> String {
132        // ASCII only: Windows PowerShell 5.1 reads BOM-less files as ANSI.
133        let mut s = String::from(
134            "# Machine GPU capability profile: prefer native over WGSL-only defaults\n",
135        );
136        for (k, v) in &self.recommended.env {
137            s.push_str(&format!("$env:{k}='{v}'\n"));
138        }
139        s.push_str(
140            "# Forge: CUDA densify decode GEMV stays lab-only unless you know the package\n",
141        );
142        s.push_str(
143            "# $env:QUALIA_LLM_CUDA_TC_DECODE='1'  # only after oracle green for that layout\n",
144        );
145        s
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn recompute_picks_fastest_coherent() {
155        let mut p = MachineGpuProfile {
156            version: 1,
157            written_unix_ms: 0,
158            host: "test".into(),
159            toolchain: Default::default(),
160            adapter: Default::default(),
161            native_tiers: vec!["cuda".into(), "vulkan".into()],
162            measured_paths: vec![
163                MeasuredDecodePath {
164                    wgpu_backend: "dx12".into(),
165                    inference_mode: "portable".into(),
166                    p64_path: "a.p64".into(),
167                    tok_s: 40.0,
168                    coherence_ok: true,
169                    tokens: 16,
170                },
171                MeasuredDecodePath {
172                    wgpu_backend: "vulkan".into(),
173                    inference_mode: "fast-verify".into(),
174                    p64_path: "a.p64".into(),
175                    tok_s: 100.0,
176                    coherence_ok: true,
177                    tokens: 16,
178                },
179                MeasuredDecodePath {
180                    wgpu_backend: "dx12".into(),
181                    inference_mode: "cuda".into(),
182                    p64_path: "a.p64".into(),
183                    tok_s: 200.0,
184                    coherence_ok: false,
185                    tokens: 16,
186                },
187            ],
188            recommended: Default::default(),
189            notes: vec![],
190        };
191        p.recompute_recommended();
192        assert_eq!(p.recommended.wgpu_backend, "vulkan");
193        assert_eq!(p.recommended.inference_mode, "fast-verify");
194
195        // Windows PowerShell 5.1 reads BOM-less files as ANSI: non-ASCII would render as mojibake.
196        let ps1 = p.apply_env_script_ps1();
197        assert!(ps1.is_ascii(), "apply script must be ASCII: {ps1}");
198        assert!(ps1.contains("$env:QUALIA_WGPU_BACKEND='vulkan'"));
199        assert!(ps1.contains("$env:QUALIA_INFERENCE_MODE='fast-verify'"));
200    }
201}