qualia_core_db/inference/lab/
device_roof.rs1use std::time::Instant;
4
5use crate::device_benchmark::benchmark_devices;
6
7#[derive(Debug, Clone)]
8pub struct DeviceRoof {
9 pub gemv_n: usize,
10 pub best_label: String,
11 pub best_backend: String,
12 pub gemv_ms: f64,
13 pub gemv_gflops: f64,
14 pub upload_gbps: f64,
15 pub balance_flop_per_byte: f64,
16 pub notes: String,
17}
18
19pub fn calibrate_device_roof(gemv_n: usize) -> DeviceRoof {
21 let n = gemv_n.max(256).min(4096);
22 let matrix = benchmark_devices(n);
23 let best = matrix.best();
24 let (label, backend, ms, gflops, up) = match best {
25 Some(c) => (
26 c.label.clone(),
27 c.backend.clone(),
28 c.ms_per_gemv,
29 c.gflops,
30 c.upload_gbps,
31 ),
32 None => ("none".into(), "none".into(), f64::INFINITY, 0.0, 0.0),
33 };
34 let balance = if up.is_finite() && up > 0.1 {
37 (gflops.max(0.01)) / up
39 } else {
40 20.0
42 };
43 DeviceRoof {
44 gemv_n: n,
45 best_label: label,
46 best_backend: backend,
47 gemv_ms: ms,
48 gemv_gflops: gflops,
49 upload_gbps: up,
50 balance_flop_per_byte: balance,
51 notes: format!(
52 "from benchmark_devices({n}); use balance_flop_per_byte for schedule classification"
53 ),
54 }
55}
56
57impl DeviceRoof {
58 pub fn format_report(&self) -> String {
59 format!(
60 "Device roof calibration\n gemv_n: {}\n best: {} [{}]\n gemv_ms: {:.4}\n gemv_gflops: {:.2}\n upload_gbps: {:.2}\n balance FLOP/B:{:.2}\n {}\n",
61 self.gemv_n,
62 self.best_label,
63 self.best_backend,
64 self.gemv_ms,
65 self.gemv_gflops,
66 if self.upload_gbps.is_finite() {
67 self.upload_gbps
68 } else {
69 -1.0
70 },
71 self.balance_flop_per_byte,
72 self.notes
73 )
74 }
75}
76
77pub fn cpu_q4_intensity_probe(n_in: usize, n_out: usize) -> (f64, f64) {
79 use crate::ggml_quants::{
80 dequantize_row_into, q4k_block_to_soa, BLOCK_Q4K_SOA_BYTES, GGML_TYPE_Q4_K_SOA,
81 };
82 let n_in = n_in.max(256) & !255; let n_out = n_out.max(1).min(64);
84 let mut stock = [0u8; 144];
85 stock[0] = 0x00;
86 stock[1] = 0x3c;
87 for i in 4..144 {
88 stock[i] = (i as u8).wrapping_mul(17);
89 }
90 let mut soa = [0u8; BLOCK_Q4K_SOA_BYTES];
91 let _ = q4k_block_to_soa(&stock, &mut soa);
92 let n_blocks = n_in / 256;
93 let mut weight = Vec::with_capacity(n_out * n_blocks * BLOCK_Q4K_SOA_BYTES);
94 for _ in 0..n_out {
95 for _ in 0..n_blocks {
96 weight.extend_from_slice(&soa);
97 }
98 }
99 let x: Vec<f32> = (0..n_in).map(|i| (i as f32) * 0.001).collect();
100 let mut row = vec![0.0f32; n_in];
101 let t0 = Instant::now();
102 let mut acc = 0.0f64;
103 for r in 0..n_out {
104 let off = r * n_blocks * BLOCK_Q4K_SOA_BYTES;
105 dequantize_row_into(
106 &weight[off..off + n_blocks * BLOCK_Q4K_SOA_BYTES],
107 GGML_TYPE_Q4_K_SOA,
108 n_in,
109 &mut row,
110 )
111 .ok();
112 acc += row
113 .iter()
114 .zip(x.iter())
115 .map(|(a, b)| (*a as f64) * (*b as f64))
116 .sum::<f64>();
117 }
118 let secs = t0.elapsed().as_secs_f64().max(1e-9);
119 let flops = (n_in as f64) * (n_out as f64) * 2.0; let bytes = (weight.len() + n_in * 4 + n_out * 4) as f64;
121 let gflops = (flops / secs) / 1e9;
122 let intensity = flops / bytes;
123 let _ = acc;
124 (gflops, intensity)
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn cpu_intensity_positive() {
133 let (g, i) = cpu_q4_intensity_probe(256, 4);
134 assert!(g > 0.0);
135 assert!(i > 0.0);
136 }
137}