1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct Val(pub u32);
12
13pub type W = usize;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum Rows {
19 Tokens,
21 Seqs,
23 Markers,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct Shape {
30 pub rows: Rows,
32 pub width: usize,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum Epilogue {
39 None,
41 Gelu,
43 Relu,
45 Accumulate,
47}
48
49#[derive(Debug, Clone, PartialEq)]
51pub enum Op {
52 Embed {
54 table: W,
56 out: Val,
58 },
59 LayerNorm {
61 x: Val,
63 w: W,
65 b: Option<W>,
67 eps: f64,
69 out: Val,
71 },
72 Gemm {
74 a: Val,
76 w: W,
78 b: Option<W>,
80 epilogue: Epilogue,
82 out: Val,
84 },
85 Rope {
88 qkv: Val,
90 theta: f64,
92 },
93 Attention {
95 qkv: Val,
97 window: Option<usize>,
99 out: Val,
101 },
102 GeGlu {
104 x: Val,
106 out: Val,
108 },
109 AddType {
111 h: Val,
113 table: W,
115 },
116 GatherMarkers {
118 h: Val,
120 out: Val,
122 },
123 ActFeatures {
126 h: Val,
128 logits: Val,
130 out: Val,
132 },
133}
134
135impl Op {
136 #[must_use]
138 pub fn uses(&self) -> (Vec<Val>, Vec<Val>) {
139 match *self {
140 Op::Embed { out, .. } => (vec![], vec![out]),
141 Op::LayerNorm { x, out, .. } | Op::GeGlu { x, out } => (vec![x], vec![out]),
142 Op::Gemm { a, epilogue, out, .. } => {
143 if epilogue == Epilogue::Accumulate {
144 (vec![a, out], vec![out])
145 } else {
146 (vec![a], vec![out])
147 }
148 }
149 Op::Rope { qkv, .. } => (vec![qkv], vec![qkv]),
150 Op::Attention { qkv, out, .. } => (vec![qkv], vec![out]),
151 Op::AddType { h, .. } => (vec![h], vec![h]),
152 Op::GatherMarkers { h, out } => (vec![h], vec![out]),
153 Op::ActFeatures { h, logits, out } => (vec![h, logits], vec![out]),
154 }
155 }
156}
157
158#[derive(Debug, Clone, Default, PartialEq)]
160pub struct Graph {
161 pub vals: Vec<Shape>,
163 pub ops: Vec<Op>,
165 pub logits: Option<Val>,
167 pub act: Option<Val>,
169}
170
171impl Graph {
172 pub fn val(&mut self, rows: Rows, width: usize) -> Val {
178 self.vals.push(Shape { rows, width });
179 Val(u32::try_from(self.vals.len() - 1).expect("fewer than 2^32 values"))
180 }
181
182 #[must_use]
184 pub fn shape(&self, v: Val) -> Shape {
185 self.vals[v.0 as usize]
186 }
187
188 pub fn push(&mut self, op: Op) {
190 self.ops.push(op);
191 }
192}
193
194#[derive(Debug, Clone, PartialEq, Eq)]
196pub struct Layout {
197 pub offsets: Vec<usize>,
199 pub len: usize,
201}
202
203pub const ALIGN: usize = 16;
205
206#[must_use]
212pub fn layout(graph: &Graph, rows: impl Fn(Rows) -> usize) -> Layout {
213 let n = graph.vals.len();
214 let mut live = vec![(usize::MAX, 0usize); n];
215 for (i, op) in graph.ops.iter().enumerate() {
216 let (r, w) = op.uses();
217 for v in r.into_iter().chain(w) {
218 let l = &mut live[v.0 as usize];
219 l.0 = l.0.min(i);
220 l.1 = l.1.max(i);
221 }
222 }
223 for v in [graph.logits, graph.act].into_iter().flatten() {
224 let l = &mut live[v.0 as usize];
225 l.0 = l.0.min(graph.ops.len());
226 l.1 = usize::MAX;
227 }
228 let size: Vec<usize> =
229 graph.vals.iter().map(|s| (rows(s.rows) * s.width).next_multiple_of(ALIGN)).collect();
230 let mut order: Vec<usize> = (0..n).filter(|&v| live[v].0 != usize::MAX).collect();
231 order.sort_by_key(|&v| (std::cmp::Reverse(size[v]), v));
232 let mut offsets = vec![0usize; n];
233 let mut placed: Vec<usize> = Vec::new();
234 let mut len = 0;
235 for v in order {
236 let (a, b) = live[v];
237 let mut taken: Vec<(usize, usize)> = placed
238 .iter()
239 .filter(|&&u| live[u].0 <= b && a <= live[u].1)
240 .map(|&u| (offsets[u], offsets[u] + size[u]))
241 .collect();
242 taken.sort_unstable();
243 let mut at = 0;
244 for (lo, hi) in taken {
245 if at + size[v] <= lo {
246 break;
247 }
248 at = at.max(hi);
249 }
250 offsets[v] = at;
251 len = len.max(at + size[v]);
252 placed.push(v);
253 }
254 Layout { offsets, len }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260
261 fn chain(width: usize, steps: usize) -> Graph {
262 let mut g = Graph::default();
263 let mut x = g.val(Rows::Tokens, width);
264 g.push(Op::Embed { table: 0, out: x });
265 for _ in 0..steps {
266 let y = g.val(Rows::Tokens, width);
267 g.push(Op::LayerNorm { x, w: 1, b: None, eps: 1e-5, out: y });
268 x = y;
269 }
270 g.logits = Some(x);
271 g
272 }
273
274 #[test]
275 fn a_chain_needs_two_buffers() {
276 let g = chain(10, 20);
277 let l = layout(&g, |_| 7);
278 let one = (7 * 10usize).next_multiple_of(ALIGN);
279 assert_eq!(l.len, 2 * one);
280 for (i, op) in g.ops.iter().enumerate().skip(1) {
281 let Op::LayerNorm { x, out, .. } = *op else { unreachable!() };
282 assert_ne!(l.offsets[x.0 as usize], l.offsets[out.0 as usize], "op {i} aliases");
283 }
284 }
285
286 #[test]
287 fn live_values_never_overlap() {
288 let mut g = Graph::default();
290 let h = g.val(Rows::Tokens, 8);
291 g.push(Op::Embed { table: 0, out: h });
292 for i in 0..6 {
293 let a = g.val(Rows::Tokens, 8 * (i % 3 + 1));
294 let b = g.val(Rows::Seqs, 3);
295 g.push(Op::Gemm { a: h, w: 0, b: None, epilogue: Epilogue::None, out: a });
296 g.push(Op::GatherMarkers { h: a, out: b });
297 g.push(Op::Gemm { a, w: 0, b: None, epilogue: Epilogue::Accumulate, out: h });
298 }
299 g.act = Some(h);
300 let rows = |r| match r {
301 Rows::Tokens => 33,
302 Rows::Seqs => 5,
303 Rows::Markers => 9,
304 };
305 let l = layout(&g, rows);
306 let mut live = vec![(usize::MAX, 0); g.vals.len()];
307 for (i, op) in g.ops.iter().enumerate() {
308 let (r, w) = op.uses();
309 for v in r.into_iter().chain(w) {
310 let x = &mut live[v.0 as usize];
311 *x = (x.0.min(i), x.1.max(i));
312 }
313 }
314 live[h.0 as usize].1 = usize::MAX;
315 for u in 0..g.vals.len() {
316 for v in 0..u {
317 let time = live[u].0 <= live[v].1 && live[v].0 <= live[u].1;
318 let su = rows(g.vals[u].rows) * g.vals[u].width;
319 let sv = rows(g.vals[v].rows) * g.vals[v].width;
320 let space = l.offsets[u] < l.offsets[v] + sv && l.offsets[v] < l.offsets[u] + su;
321 assert!(!(time && space), "values {u} and {v} overlap");
322 assert!(l.offsets[u] + su <= l.len);
323 }
324 }
325 assert!(l.len < g.vals.iter().map(|s| rows(s.rows) * s.width).sum::<usize>());
326 }
327}