metrale_model_layers/circuit_exec/
verify_batch.rs1use std::collections::BTreeMap;
18use std::sync::Arc;
19
20use anyhow::{Context, Result, ensure};
21use metrale_circuit::planner::plan_buffers_with;
22use metrale_circuit::{AvailableKernels, Circuit, FusionPlan, Policy, RowTable, Rule, fuse_table};
23use metrale_config::ModelConfig;
24use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend};
25use parking_lot::Mutex;
26
27use super::bindings::{CircuitLayer, HeadBinding};
28use super::compile::{self, Fixed};
29use super::kernels::KernelTable;
30use super::program::Program;
31
32pub const CACHE_CAP: usize = 256;
34
35pub struct VerifyBatchProgram {
37 pub program: Program,
38 pub plan: FusionPlan,
39}
40
41pub struct VerifyBatch {
43 circuit: Circuit,
44 rules: Vec<Rule>,
45 available: AvailableKernels,
46 policy: Policy,
47 kernels: KernelTable,
48 fixed: Fixed,
49 layers: Vec<CircuitLayer>,
50 head: HeadBinding,
51 config: ModelConfig,
52 workspace: DevicePtr,
53 workspace_bytes: u64,
54 pub max_rows: u64,
56 cache: Mutex<(u64, BTreeMap<RowTable, (u64, Arc<VerifyBatchProgram>)>)>,
57}
58
59pub fn sizing_tables(max_rows: u64) -> Vec<RowTable> {
62 (4..=max_rows)
63 .map(|r| {
64 let text = if r % 2 == 0 {
65 format!("2x{}", r / 2)
66 } else {
67 let tail = (r - 3) / 2;
69 format!("3x1! 2x{tail}{}", if tail == 1 { "!" } else { "" })
70 };
71 RowTable::parse(&text).expect("a well-formed sizing table")
72 })
73 .collect()
74}
75
76pub fn arena_bytes(
78 circuit: &Circuit,
79 rules: &[Rule],
80 available: &AvailableKernels,
81 policy: &Policy,
82 table: &RowTable,
83) -> Result<u64> {
84 let plan = fuse_table(circuit, rules, available, policy, table)
85 .with_context(|| format!("fusing the batched verify of `{table}`"))?;
86 let layout = compile::layout(circuit, &plan)?;
87 Ok(plan_buffers_with(circuit, &plan, plan.rows, &layout)?.arena_bytes)
88}
89
90pub struct Parts {
92 pub circuit: Circuit,
93 pub rules: Vec<Rule>,
94 pub available: AvailableKernels,
95 pub policy: Policy,
96 pub kernels: KernelTable,
97 pub fixed: Fixed,
98 pub layers: Vec<CircuitLayer>,
99 pub head: HeadBinding,
100 pub config: ModelConfig,
101}
102
103impl VerifyBatch {
104 pub fn new(parts: Parts, workspace: DevicePtr, workspace_bytes: u64, max_rows: u64) -> Self {
107 let Parts {
108 circuit,
109 rules,
110 available,
111 policy,
112 kernels,
113 fixed,
114 layers,
115 head,
116 config,
117 } = parts;
118 VerifyBatch {
119 circuit,
120 rules,
121 available,
122 policy,
123 kernels,
124 fixed,
125 layers,
126 head,
127 config,
128 workspace,
129 workspace_bytes,
130 max_rows,
131 cache: Mutex::new((0, BTreeMap::new())),
132 }
133 }
134
135 pub fn program(
137 &self,
138 gpu: &dyn GpuBackend,
139 table: &RowTable,
140 ) -> Result<Arc<VerifyBatchProgram>> {
141 ensure!(
142 table.rows() <= self.max_rows,
143 "a batched verify of {} rows is past the {} this executor was sized for",
144 table.rows(),
145 self.max_rows
146 );
147 {
148 let mut c = self.cache.lock();
149 c.0 += 1;
150 let tick = c.0;
151 if let Some(e) = c.1.get_mut(table) {
152 e.0 = tick;
153 return Ok(e.1.clone());
154 }
155 }
156 let plan = fuse_table(
157 &self.circuit,
158 &self.rules,
159 &self.available,
160 &self.policy,
161 table,
162 )
163 .with_context(|| format!("fusing the batched verify of `{table}`"))?;
164 let layout = compile::layout(&self.circuit, &plan)?;
165 let buffers = plan_buffers_with(&self.circuit, &plan, plan.rows, &layout)?;
166 ensure!(
167 buffers.arena_bytes <= self.workspace_bytes,
168 "the batched verify of `{table}` needs {} workspace bytes; the build sized {}",
169 buffers.arena_bytes,
170 self.workspace_bytes
171 );
172 let inputs = compile::Inputs {
173 gpu,
174 config: &self.config,
175 kernels: &self.kernels,
176 fixed: &self.fixed,
177 layers: &self.layers,
178 head: &self.head,
179 draft: None,
180 };
181 let program = compile::compile(
182 &self.circuit,
183 &plan,
184 &layout,
185 &buffers,
186 self.workspace,
187 &inputs,
188 )
189 .with_context(|| format!("compiling the batched verify of `{table}`"))?;
190 let entry = Arc::new(VerifyBatchProgram { program, plan });
191 let mut c = self.cache.lock();
192 let tick = c.0;
193 if c.1.len() >= CACHE_CAP
194 && let Some(oldest) =
195 c.1.iter()
196 .min_by_key(|(_, (t, _))| *t)
197 .map(|(k, _)| k.clone())
198 {
199 c.1.remove(&oldest);
200 }
201 c.1.insert(table.clone(), (tick, entry.clone()));
202 Ok(entry)
203 }
204
205 pub fn fixed_meta(&self) -> &crate::layer::AttnMetadataDev {
207 &self.fixed.verify_batch_meta
208 }
209
210 pub fn fixed_tokens(&self) -> DevicePtr {
212 self.fixed.verify_batch_tokens
213 }
214
215 pub fn cached(&self) -> usize {
217 self.cache.lock().1.len()
218 }
219}