1use std::collections::{BTreeMap, BTreeSet};
24
25use crate::fuser::{EdgeState, FusionPlan};
26use crate::ir::{Circuit, EdgeIdx};
27
28pub const ALIGN: u64 = 256;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct Slot {
34 pub edge: EdgeIdx,
36 pub offset: u64,
38 pub bytes: u64,
40 pub start: usize,
42 pub end: usize,
44 pub row_stride: u64,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct BufferPlan {
52 pub slots: Vec<Slot>,
54 pub arena_bytes: u64,
56 pub materialized_bytes: u64,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
62pub enum PlanError {
63 #[error("edge `{0}` has no byte size at the sized row count")]
65 Size(String),
66 #[error("layout: {0}")]
68 Layout(String),
69}
70
71#[derive(Debug, Clone, Default, PartialEq, Eq)]
73pub struct Layout {
74 pub external: BTreeSet<EdgeIdx>,
76 pub aliases: Vec<(EdgeIdx, EdgeIdx)>,
79 pub packs: Vec<Vec<EdgeIdx>>,
81 pub row_packs: Vec<RowPack>,
83}
84
85#[derive(Debug, Clone, Default, PartialEq, Eq)]
90pub struct RowPack {
91 pub members: Vec<EdgeIdx>,
93 pub over: Option<EdgeIdx>,
95}
96
97pub fn live_ranges(circuit: &Circuit, plan: &FusionPlan) -> BTreeMap<EdgeIdx, (usize, usize)> {
99 let mut group_of = vec![None; circuit.nodes.len()];
100 for (g, grp) in plan.groups.iter().enumerate() {
101 for &n in &grp.nodes {
102 group_of[n] = Some(g);
103 }
104 }
105 let last = plan.groups.len().saturating_sub(1);
106 let mut out = BTreeMap::new();
107 for (e, edge) in circuit.edges.iter().enumerate() {
108 if plan.edge_states[e] != Some(EdgeState::Materialized) {
109 continue;
110 }
111 let start = edge.producer.and_then(|p| group_of[p]).unwrap_or(0);
112 let end = if edge.is_output {
113 last
114 } else {
115 edge.consumers
116 .iter()
117 .filter_map(|&c| group_of[c])
118 .max()
119 .unwrap_or(last)
120 .max(start)
121 };
122 out.insert(e, (start, end));
123 }
124 out
125}
126
127pub fn plan_buffers(
130 circuit: &Circuit,
131 plan: &FusionPlan,
132 max_rows: u64,
133) -> Result<BufferPlan, PlanError> {
134 plan_buffers_with(circuit, plan, max_rows, &Layout::default())
135}
136
137struct Item {
139 classes: Vec<(EdgeIdx, u64)>,
141 bytes: u64,
142 start: usize,
143 end: usize,
144 key: EdgeIdx,
146}
147
148fn root(parent: &mut BTreeMap<EdgeIdx, EdgeIdx>, e: EdgeIdx) -> EdgeIdx {
149 let mut r = e;
150 while parent[&r] != r {
151 r = parent[&r];
152 }
153 parent.insert(e, r);
154 r
155}
156
157pub fn plan_buffers_with(
165 circuit: &Circuit,
166 plan: &FusionPlan,
167 max_rows: u64,
168 layout: &Layout,
169) -> Result<BufferPlan, PlanError> {
170 let mut dims = circuit.dims.clone();
171 dims.insert("n".into(), max_rows);
172 let ranges = live_ranges(circuit, plan);
173 let id = |e: EdgeIdx| circuit.edges.get(e).map_or("?", |x| x.id.as_str());
174 let layout_err = |msg: String| PlanError::Layout(msg);
175 for &e in layout
176 .external
177 .iter()
178 .chain(layout.aliases.iter().flat_map(|(a, b)| [a, b]))
179 .chain(layout.packs.iter().flatten())
180 .chain(
181 layout
182 .row_packs
183 .iter()
184 .flat_map(|p| p.members.iter().chain(&p.over)),
185 )
186 {
187 if !ranges.contains_key(&e) {
188 return Err(layout_err(format!("edge `{}` is not materialised", id(e))));
189 }
190 }
191 let mut size = BTreeMap::new();
192 let mut rows_of = BTreeMap::new();
193 let mut materialized_bytes = 0u64;
194 for &e in ranges.keys() {
195 let edge = &circuit.edges[e];
196 let size_err = || PlanError::Size(edge.id.clone());
197 let rows = edge.rows.eval(&dims).map_err(|_| size_err())?;
198 let bytes = plan.edge_formats[e]
199 .bytes(rows, edge.dim_value)
200 .ok_or_else(size_err)?;
201 materialized_bytes = materialized_bytes.checked_add(bytes).ok_or_else(size_err)?;
202 if !layout.external.contains(&e) {
203 size.insert(e, bytes);
204 rows_of.insert(e, rows.max(1));
205 }
206 }
207 let mut parent: BTreeMap<EdgeIdx, EdgeIdx> = size.keys().map(|&e| (e, e)).collect();
208 for &(out, inp) in &layout.aliases {
209 if layout.external.contains(&out) || layout.external.contains(&inp) {
210 return Err(layout_err(format!(
211 "alias `{}` over `{}` names an external edge",
212 id(out),
213 id(inp)
214 )));
215 }
216 if size[&out] != size[&inp] {
217 return Err(layout_err(format!(
218 "alias `{}` ({} B) over `{}` ({} B): sizes differ",
219 id(out),
220 size[&out],
221 id(inp),
222 size[&inp]
223 )));
224 }
225 let (a, b) = (root(&mut parent, out), root(&mut parent, inp));
226 parent.insert(a.max(b), a.min(b));
227 }
228 let edges: Vec<EdgeIdx> = size.keys().copied().collect();
229 let mut class_range: BTreeMap<EdgeIdx, (usize, usize, EdgeIdx)> = BTreeMap::new();
230 for &e in &edges {
231 let r = root(&mut parent, e);
232 let (s, t) = ranges[&e];
233 let c = class_range.entry(r).or_insert((s, t, e));
234 *c = (c.0.min(s), c.1.max(t), c.2.min(e));
235 }
236 let mut packed = BTreeSet::new();
237 let mut items = Vec::new();
238 let mut stride_of: BTreeMap<EdgeIdx, u64> = BTreeMap::new();
239 for pack in &layout.row_packs {
240 let rows = pack
241 .members
242 .first()
243 .map_or(0, |e| rows_of.get(e).copied().unwrap_or(0));
244 let mut item = Item {
245 classes: Vec::new(),
246 bytes: 0,
247 start: usize::MAX,
248 end: 0,
249 key: EdgeIdx::MAX,
250 };
251 let mut row_bytes = 0u64;
252 for &e in pack.members.iter().chain(&pack.over) {
253 if layout.external.contains(&e) {
254 return Err(layout_err(format!(
255 "row pack names external edge `{}`",
256 id(e)
257 )));
258 }
259 if rows_of[&e] != rows {
260 return Err(layout_err(format!(
261 "row-packed edge `{}` has another row count",
262 id(e)
263 )));
264 }
265 let r = root(&mut parent, e);
266 if !packed.insert(r) {
267 return Err(layout_err(format!("edge `{}` is packed twice", id(e))));
268 }
269 let (s, t, k) = class_range[&r];
270 let within = if Some(e) == pack.over { 0 } else { row_bytes };
271 item.classes.push((r, within));
272 if Some(e) != pack.over {
273 row_bytes += size[&e] / rows;
274 }
275 item.start = item.start.min(s);
276 item.end = item.end.max(t);
277 item.key = item.key.min(k);
278 }
279 item.bytes = row_bytes * rows;
280 if let Some(o) = pack.over
281 && size[&o] != item.bytes
282 {
283 return Err(layout_err(format!(
284 "row pack over `{}` ({} B) holds {} B",
285 id(o),
286 size[&o],
287 item.bytes
288 )));
289 }
290 for &(r, _) in &item.classes {
291 stride_of.insert(r, row_bytes);
292 }
293 items.push(item);
294 }
295 for pack in &layout.packs {
296 let mut item = Item {
297 classes: Vec::new(),
298 bytes: 0,
299 start: usize::MAX,
300 end: 0,
301 key: EdgeIdx::MAX,
302 };
303 for &e in pack {
304 if layout.external.contains(&e) {
305 return Err(layout_err(format!("pack names external edge `{}`", id(e))));
306 }
307 let r = root(&mut parent, e);
308 if !packed.insert(r) {
309 return Err(layout_err(format!("edge `{}` is packed twice", id(e))));
310 }
311 let (s, t, k) = class_range[&r];
312 item.classes.push((r, item.bytes));
313 item.bytes += size[&e];
314 item.start = item.start.min(s);
315 item.end = item.end.max(t);
316 item.key = item.key.min(k);
317 }
318 items.push(item);
319 }
320 for (&r, &(s, t, k)) in &class_range {
321 if !packed.contains(&r) {
322 items.push(Item {
323 classes: vec![(r, 0)],
324 bytes: size[&r],
325 start: s,
326 end: t,
327 key: k,
328 });
329 }
330 }
331 items.sort_by(|a, b| {
332 a.start
333 .cmp(&b.start)
334 .then(b.bytes.cmp(&a.bytes))
335 .then(a.key.cmp(&b.key))
336 });
337 let mut placed: Vec<(u64, u64, usize, usize)> = Vec::with_capacity(items.len());
338 let mut class_at: BTreeMap<EdgeIdx, (u64, usize, usize)> = BTreeMap::new();
339 let mut arena_bytes = 0u64;
340 for item in &items {
341 let mut busy: Vec<(u64, u64)> = placed
342 .iter()
343 .filter(|p| p.2 <= item.end && item.start <= p.3)
344 .map(|p| (p.0, p.1))
345 .collect();
346 busy.sort_unstable();
347 let mut offset = 0u64;
348 for (lo, hi) in busy {
349 if offset + item.bytes <= lo {
350 break;
351 }
352 offset = offset.max(hi.div_ceil(ALIGN) * ALIGN);
353 }
354 arena_bytes = arena_bytes.max(offset + item.bytes);
355 placed.push((offset, offset + item.bytes, item.start, item.end));
356 for &(r, within) in &item.classes {
357 class_at.insert(r, (offset + within, item.start, item.end));
358 }
359 }
360 let mut slots = Vec::with_capacity(edges.len());
361 for e in edges {
362 let r = root(&mut parent, e);
363 let (offset, start, end) = class_at[&r];
364 slots.push(Slot {
365 edge: e,
366 offset,
367 bytes: size[&e],
368 start,
369 end,
370 row_stride: stride_of.get(&r).copied().unwrap_or(size[&e] / rows_of[&e]),
371 });
372 }
373 Ok(BufferPlan {
374 slots,
375 arena_bytes,
376 materialized_bytes,
377 })
378}
379
380#[cfg(test)]
381#[path = "planner_tests.rs"]
382mod planner_tests;