1pub mod arena;
27pub mod branches;
28pub mod emit;
29pub mod graph;
30pub mod kernels;
31pub mod plan;
32pub mod residence;
33pub mod weights;
34
35use std::collections::BTreeMap;
36use std::fmt;
37use std::path::Path;
38
39use crate::codegen_cuda::knobs::CudaKnobs;
40use crate::model::author::{self, NotYet};
41use crate::model::program::{self, Cut, Identity};
42use crate::model::gguf::Gguf;
43use crate::model::spec::{Mixer, ModelSpec};
44use crate::spatial::passes::recognize_stages::recognize_stages;
45
46use emit::{Checkpoint, Program};
47use graph::{GraphError, Role, StepGraph};
48use plan::{Schedule, SeqIo, StepIo};
49
50#[derive(Clone, Debug, PartialEq, Eq)]
52pub enum LaneError {
53 NotYet(NotYet),
55 Parse(String),
57 Graph(GraphError),
59 Unbound(String),
61 Shape { var: String, declared: usize, bound: usize },
63 Format { tensor: String, ty: &'static str, why: &'static str },
65 NoKernel { op: usize, kind: &'static str, out: String, why: String },
67 Chunk { rows: usize, ctx: usize },
69 Batch { batch: usize },
71 Io(String),
73 Memory { needed: usize, budget: usize },
75}
76
77impl fmt::Display for LaneError {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 match self {
80 LaneError::NotYet(n) => write!(f, "{n}"),
81 LaneError::Parse(e) => write!(f, "the authored step does not parse: {e}"),
82 LaneError::Graph(e) => write!(f, "{e}"),
83 LaneError::Unbound(v) => write!(f, "nothing binds `{v}`"),
84 LaneError::Shape { var, declared, bound } => {
85 write!(f, "`{var}` is declared with {declared} values and bound to {bound}")
86 }
87 LaneError::Format { tensor, ty, why } => write!(f, "`{tensor}` is {ty}: {why}"),
88 LaneError::NoKernel { op, kind, out, why } => write!(f, "op {op} ({kind} into `{out}`) has no kernel: {why}"),
89 LaneError::Chunk { rows, ctx } => write!(
90 f,
91 "a prefill chunk of {rows} rows: a chunk is a power of two up to {} rows and the caches' {ctx} positions",
92 plan::MAX_CHUNK
93 ),
94 LaneError::Batch { batch } => write!(f, "a batch of {batch}: a batch step decodes two sequences or more"),
95 LaneError::Io(e) => write!(f, "the checkpoint: {e}"),
96 LaneError::Memory { needed, budget } => {
97 write!(f, "the caches, arena and buffers take {needed} bytes of the device's {budget}, every matrix moved to the host")
98 }
99 }
100 }
101}
102
103impl std::error::Error for LaneError {}
104
105impl From<GraphError> for LaneError {
106 fn from(e: GraphError) -> Self {
107 LaneError::Graph(e)
108 }
109}
110
111#[derive(Clone, Copy, Debug, PartialEq, Eq)]
114pub struct LaneOptions {
115 pub ctx: usize,
116 pub knobs: CudaKnobs,
117 pub prefill: usize,
118}
119
120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
123pub enum FormKind {
124 Decode,
125 Prefill,
126 Batch,
127}
128
129#[derive(Clone, Debug)]
132pub struct Form {
133 pub rows: usize,
134 pub kind: FormKind,
135 pub identity: Identity,
137 pub source: String,
138 pub graph: StepGraph,
139 pub schedule: Schedule,
140 pub arena: arena::Arena,
141}
142
143#[derive(Clone, Debug)]
145pub struct Lane {
146 pub forms: Vec<Form>,
147 pub program: Program,
148 pub plan: residence::Plan,
150 pub residence: residence::Residence,
153}
154
155impl Form {
156 pub fn tag(&self) -> String {
159 match self.kind {
160 FormKind::Batch => format!("b{}", self.rows),
161 FormKind::Decode | FormKind::Prefill => self.rows.to_string(),
162 }
163 }
164}
165
166impl Lane {
167 pub fn decode(&self) -> &Form {
169 &self.forms[0]
170 }
171
172 pub fn chunk(&self, rows: usize) -> Option<&Form> {
174 self.forms.iter().find(|f| f.kind == FormKind::Prefill && f.rows == rows)
175 }
176
177 pub fn batch(&self) -> Option<&Form> {
179 self.forms.iter().find(|f| f.kind == FormKind::Batch)
180 }
181}
182
183pub fn decode_io() -> StepIo {
185 StepIo { token: "tok".into(), position: "pos".into(), logits: "logits".into(), next: "next".into(), more: Vec::new() }
186}
187
188pub fn prefill_io() -> StepIo {
190 StepIo { token: "toks".into(), position: "pos_s0".into(), logits: "logits".into(), next: "next".into(), more: Vec::new() }
191}
192
193pub fn batch_io(rows: usize) -> StepIo {
196 StepIo {
197 token: "toks".into(),
198 position: "pos_s0".into(),
199 logits: "logits_s0".into(),
200 next: "next_s0".into(),
201 more: (1..rows).map(|b| SeqIo { position: format!("pos_s{b}"), logits: format!("logits_s{b}"), next: format!("next_s{b}") }).collect(),
202 }
203}
204
205fn form(
207 spec: &ModelSpec,
208 g: &mut Gguf,
209 rows: usize,
210 kind: FormKind,
211 cut: Cut,
212 opts: &LaneOptions,
213 io: StepIo,
214 held: &[&str],
215) -> Result<Form, LaneError> {
216 let ((source, _, q8), identity) = program::written(spec, cut);
217 let rp = recognize_stages(&source).map_err(|e| LaneError::Parse(format!("{e:?}")))?;
218 let graph = StepGraph::build(&rp)?;
219 let mut constants = BTreeMap::new();
220 for t in graph.of_role(Role::Constant) {
221 let placed = weights::place(spec, g, &t.name, t.len(), q8.contains_key(&t.name), opts.ctx)?;
222 constants.insert(t.name.clone(), placed);
223 }
224 let schedule = plan::plan(&graph, &|s| rp.name(s).to_string(), constants, opts.knobs.activations, &opts.knobs, io, held)?;
225 let arena = arena::place(&schedule);
226 Ok(Form { rows, kind, identity, source, graph, schedule, arena })
227}
228
229pub fn compile(spec: &ModelSpec, checkpoint: &Path, opts: &LaneOptions) -> Result<Lane, LaneError> {
231 compile_holding(spec, checkpoint, opts, &[])
232}
233
234pub fn compile_holding(spec: &ModelSpec, checkpoint: &Path, opts: &LaneOptions, held: &[&str]) -> Result<Lane, LaneError> {
237 compile_forms(spec, checkpoint, opts, 1, held)
238}
239
240pub fn compile_batch(spec: &ModelSpec, checkpoint: &Path, opts: &LaneOptions, batch: usize) -> Result<Lane, LaneError> {
244 compile_batch_holding(spec, checkpoint, opts, batch, &[])
245}
246
247pub fn compile_batch_holding(spec: &ModelSpec, checkpoint: &Path, opts: &LaneOptions, batch: usize, held: &[&str]) -> Result<Lane, LaneError> {
249 if batch < 2 {
250 return Err(LaneError::Batch { batch });
251 }
252 compile_forms(spec, checkpoint, opts, batch, held)
253}
254
255fn lane_check(spec: &ModelSpec) -> Result<(), NotYet> {
258 if spec.final_logit_softcap.is_some() {
259 return Err(NotYet("soft-capped logits on the CUDA lane"));
260 }
261 for layer in &spec.layers {
262 if let Mixer::Attention(a) = &layer.mixer {
263 if a.softcap.is_some() {
264 return Err(NotYet("soft-capped attention scores on the CUDA lane"));
265 }
266 if a.fused_qkv {
267 return Err(NotYet("one fused query, key and value projection on the CUDA lane"));
268 }
269 if a.value_dim != a.head_dim {
270 return Err(NotYet("values unlike the keys in width on the CUDA lane"));
271 }
272 if a.qk_norm_whole {
273 return Err(NotYet("a QK norm across every head on the CUDA lane"));
274 }
275 }
276 }
277 Ok(())
278}
279
280fn compile_forms(spec: &ModelSpec, checkpoint: &Path, opts: &LaneOptions, batch: usize, held: &[&str]) -> Result<Lane, LaneError> {
281 author::check(spec).map_err(LaneError::NotYet)?;
282 lane_check(spec).map_err(LaneError::NotYet)?;
283 if !opts.prefill.is_power_of_two() || opts.prefill > plan::MAX_CHUNK || opts.prefill > opts.ctx {
284 return Err(LaneError::Chunk { rows: opts.prefill, ctx: opts.ctx });
285 }
286 let io = |e: std::io::Error| LaneError::Io(e.to_string());
287 let mut g = Gguf::open(checkpoint).map_err(io)?;
288 let mut forms = vec![form(spec, &mut g, 1, FormKind::Decode, Cut::Decode { ctx: opts.ctx }, opts, decode_io(), held)?];
289 let mut rows = opts.prefill;
290 while rows > 1 {
291 forms.push(form(spec, &mut g, rows, FormKind::Prefill, Cut::Prefill { rows, ctx: opts.ctx }, opts, prefill_io(), held)?);
292 rows /= 2;
293 }
294 if batch > 1 {
295 forms.push(form(spec, &mut g, batch, FormKind::Batch, Cut::Batch { rows: batch, ctx: opts.ctx }, opts, batch_io(batch), held)?);
296 }
297 let ckpt = Checkpoint {
298 size: std::fs::metadata(checkpoint).map_err(io)?.len(),
299 data_start: g.data_start(),
300 digest: g.header_digest().map_err(io)?,
301 };
302 let plan = residence::plan(&forms, opts.ctx);
303 let residence = residence::place(&plan, opts.knobs.chip.mem_bytes.saturating_sub(residence::CONTEXT_RESERVE + plan.spare))?;
304 let program = emit::emit(&forms, &opts.knobs, opts.ctx, ckpt, &plan)?;
305 Ok(Lane { forms, program, plan, residence })
306}