1use std::fmt;
4
5use crate::bucket::{Bucket, Buckets};
6use crate::dtype::DType;
7use crate::plan::Graph;
8
9#[derive(Debug, Clone, Copy)]
11pub struct HostTensor<'a> {
12 pub dtype: DType,
14 pub shape: &'a [usize],
16 pub bytes: &'a [u8],
18}
19
20#[derive(Debug, Clone, Copy, Default)]
23pub struct Batch<'a> {
24 pub ids: &'a [u32],
26 pub cu: &'a [u32],
28 pub markers: &'a [u32],
30 pub mcu: &'a [u32],
32 pub qtype: &'a [u8],
34}
35
36impl Batch<'_> {
37 #[must_use]
39 pub fn seqs(&self) -> usize {
40 self.cu.len().saturating_sub(1)
41 }
42
43 pub fn check(&self, vocab: usize, types: usize) -> Result<()> {
49 let bad = |m: String| Err(Error::Batch(m));
50 let s = self.seqs();
51 if self.cu.first() != Some(&0) || self.mcu.first() != Some(&0) {
52 return bad("cu and mcu must start at 0".into());
53 }
54 if self.mcu.len() != s + 1 || self.qtype.len() != s {
55 return bad(format!(
56 "{s} sequences but {} mcu and {} qtype",
57 self.mcu.len(),
58 self.qtype.len()
59 ));
60 }
61 if self.cu[s] as usize != self.ids.len() || self.mcu[s] as usize != self.markers.len() {
62 return bad("cu and mcu must end at the totals".into());
63 }
64 for i in 0..s {
65 let (a, b) = (self.cu[i], self.cu[i + 1]);
66 if b < a || self.mcu[i + 1] < self.mcu[i] {
67 return bad(format!("sequence {i} ends before it starts"));
68 }
69 if usize::from(self.qtype[i]) >= types {
70 return bad(format!("sequence {i} has type {} of {types}", self.qtype[i]));
71 }
72 for &m in &self.markers[self.mcu[i] as usize..self.mcu[i + 1] as usize] {
73 if m >= b - a {
74 return bad(format!("marker {m} is past sequence {i} of {} tokens", b - a));
75 }
76 }
77 }
78 if let Some(&id) = self.ids.iter().find(|&&id| id as usize >= vocab) {
79 return bad(format!("token id {id} is past the vocabulary of {vocab}"));
80 }
81 Ok(())
82 }
83}
84
85#[derive(Debug, Clone, Default, PartialEq)]
87pub struct Outputs {
88 pub logits: Vec<f32>,
90 pub act: Vec<[f32; 2]>,
92 pub pooled: Vec<f32>,
94}
95
96#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct Caps {
99 pub name: &'static str,
101 pub threads: usize,
103 pub graphs: bool,
105 pub unified_memory: bool,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq)]
111pub enum Error {
112 Batch(String),
114 NoBucket {
116 stage: String,
118 tokens: usize,
120 seqs: usize,
122 markers: usize,
124 },
125 Unsupported(String),
127 Device(String),
129}
130
131impl fmt::Display for Error {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 Error::Batch(m) => write!(f, "bad batch: {m}"),
135 Error::NoBucket { stage, tokens, seqs, markers } => write!(
136 f,
137 "no {stage} bucket holds {tokens} tokens, {seqs} sequences and {markers} markers"
138 ),
139 Error::Unsupported(m) => write!(f, "unsupported: {m}"),
140 Error::Device(m) => write!(f, "device: {m}"),
141 }
142 }
143}
144
145impl std::error::Error for Error {}
146
147pub type Result<T> = std::result::Result<T, Error>;
149
150pub trait Backend: Send + Sync {
153 type Weights: Send + Sync;
155 type Plan: Send;
157
158 fn caps(&self) -> Caps;
160
161 fn upload(&self, tensors: &[HostTensor<'_>], graph: &Graph) -> Result<Self::Weights>;
167
168 fn lower(&self, w: &Self::Weights, graph: &Graph, bucket: Bucket) -> Result<Self::Plan>;
174
175 fn run(&self, plan: &mut Self::Plan, batch: &Batch<'_>, out: &mut Outputs) -> Result<()>;
181
182 fn weight_bytes(&self, _w: &Self::Weights) -> usize {
184 0
185 }
186
187 fn plan_bytes(&self, _p: &Self::Plan) -> usize {
189 0
190 }
191}
192
193#[derive(Debug)]
196pub struct Executor<B: Backend> {
197 backend: B,
198 weights: B::Weights,
199 lanes: Vec<Lane<B>>,
201 vocab: usize,
202 types: usize,
203}
204
205struct Lane<B: Backend> {
207 graph: Graph,
208 stage: String,
209 buckets: Vec<Bucket>,
210 plans: Vec<(Bucket, B::Plan)>,
211}
212
213impl<B: Backend> fmt::Debug for Lane<B> {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 let warm: Vec<Bucket> = self.plans.iter().map(|p| p.0).collect();
216 f.debug_struct("Lane")
217 .field("stage", &self.stage)
218 .field("warm", &warm)
219 .finish_non_exhaustive()
220 }
221}
222
223impl<B: Backend> Lane<B> {
224 fn new(graph: Graph, buckets: &Buckets, stage: &str) -> Self {
225 Self {
226 graph,
227 stage: stage.to_string(),
228 buckets: buckets.stage(stage).to_vec(),
229 plans: Vec::new(),
230 }
231 }
232}
233
234impl<B: Backend> Executor<B> {
235 pub fn new(
242 backend: B,
243 tensors: &[HostTensor<'_>],
244 graph: Graph,
245 buckets: &Buckets,
246 stage: &str,
247 vocab: usize,
248 types: usize,
249 ) -> Result<Self> {
250 let weights = backend.upload(tensors, &graph)?;
251 Ok(Self { backend, weights, lanes: vec![Lane::new(graph, buckets, stage)], vocab, types })
252 }
253
254 pub fn add_graph(&mut self, graph: Graph, buckets: &Buckets, stage: &str) -> usize {
258 self.lanes.push(Lane::new(graph, buckets, stage));
259 self.lanes.len() - 1
260 }
261
262 pub fn backend(&self) -> &B {
264 &self.backend
265 }
266
267 pub fn warm(&self) -> impl Iterator<Item = Bucket> + '_ {
269 self.lanes[0].plans.iter().map(|p| p.0)
270 }
271
272 pub fn memory(&self) -> (usize, usize) {
274 let plans =
275 self.lanes.iter().flat_map(|l| &l.plans).map(|p| self.backend.plan_bytes(&p.1)).sum();
276 (self.backend.weight_bytes(&self.weights), plans)
277 }
278
279 pub fn plans_mut(&mut self) -> impl Iterator<Item = &mut B::Plan> + '_ {
281 self.lanes[0].plans.iter_mut().map(|p| &mut p.1)
282 }
283
284 pub fn prepare(&mut self, bucket: Bucket) -> Result<usize> {
290 self.prepare_lane(0, bucket)
291 }
292
293 fn prepare_lane(&mut self, lane: usize, bucket: Bucket) -> Result<usize> {
294 let l = &mut self.lanes[lane];
295 if let Some(i) = l.plans.iter().position(|p| p.0 == bucket) {
296 return Ok(i);
297 }
298 let plan = self.backend.lower(&self.weights, &l.graph, bucket)?;
299 l.plans.push((bucket, plan));
300 Ok(l.plans.len() - 1)
301 }
302
303 pub fn run(&mut self, batch: &Batch<'_>, out: &mut Outputs) -> Result<Bucket> {
311 self.run_lane(0, batch, out)
312 }
313
314 pub fn run_lane(
320 &mut self,
321 lane: usize,
322 batch: &Batch<'_>,
323 out: &mut Outputs,
324 ) -> Result<Bucket> {
325 batch.check(self.vocab, self.types)?;
326 let (t, s, m) = (batch.ids.len(), batch.seqs(), batch.markers.len());
327 let l = &self.lanes[lane];
328 let bucket = l.buckets.iter().copied().find(|b| b.holds(t, s, m)).ok_or_else(|| {
329 Error::NoBucket { stage: l.stage.clone(), tokens: t, seqs: s, markers: m }
330 })?;
331 let i = self.prepare_lane(lane, bucket)?;
332 self.backend.run(&mut self.lanes[lane].plans[i].1, batch, out)?;
333 Ok(bucket)
334 }
335}
336
337#[derive(Debug, Clone, Default)]
340pub struct BatchBuf {
341 ids: Vec<u32>,
342 cu: Vec<u32>,
343 markers: Vec<u32>,
344 mcu: Vec<u32>,
345 qtype: Vec<u8>,
346}
347
348impl BatchBuf {
349 pub fn clear(&mut self) {
351 for v in [&mut self.ids, &mut self.cu, &mut self.markers, &mut self.mcu] {
352 v.clear();
353 }
354 self.qtype.clear();
355 }
356
357 pub fn push(&mut self, ids: &[u32], markers: &[u32], qtype: u8) {
363 if self.cu.is_empty() {
364 self.cu.push(0);
365 self.mcu.push(0);
366 }
367 self.ids.extend_from_slice(ids);
368 self.markers.extend_from_slice(markers);
369 self.cu.push(u32::try_from(self.ids.len()).expect("under 2^32 tokens"));
370 self.mcu.push(u32::try_from(self.markers.len()).expect("under 2^32 markers"));
371 self.qtype.push(qtype);
372 }
373
374 #[must_use]
376 pub fn batch(&self) -> Batch<'_> {
377 const EMPTY: &[u32] = &[0];
378 let (cu, mcu) =
379 if self.cu.is_empty() { (EMPTY, EMPTY) } else { (&self.cu[..], &self.mcu[..]) };
380 Batch { ids: &self.ids, cu, markers: &self.markers, mcu, qtype: &self.qtype }
381 }
382}