Skip to main content

weir/ifds_resident_types/
dispatch.rs

1use smallvec::SmallVec;
2use vyre_foundation::ir::Program;
3
4/// Backend-neutral resident dispatch hooks for IFDS.
5///
6/// Weir owns this trait so production dataflow APIs do not depend on concrete
7/// driver crates. A Vyre/CUDA adapter can implement it by forwarding to
8/// `VyreBackend::{allocate_resident, upload_resident, dispatch_resident_timed,
9/// download_resident, free_resident}`.
10pub trait IfdsResidentDispatch {
11    /// Opaque backend-owned resident handle.
12    type Resource: Clone + PartialEq;
13
14    /// Stable backend/device identity for resident resource ownership.
15    fn resident_backend_id(&self) -> &'static str;
16
17    /// Stable backend implementation version for resident cache compatibility.
18    ///
19    /// Implementers backed by `vyre::VyreBackend` should forward
20    /// `vyre::VyreBackend::version`. The default keeps existing non-Vyre
21    /// test dispatchers source-compatible while preventing accidental reuse
22    /// across dispatchers that opt into versioned identity.
23    fn resident_backend_version(&self) -> &'static str {
24        "unspecified"
25    }
26
27    /// Allocate a resident buffer of exactly `byte_len` bytes.
28    fn allocate_resident(&self, byte_len: usize) -> Result<Self::Resource, String>;
29
30    /// Upload bytes into a resident buffer.
31    fn upload_resident(&self, resource: &Self::Resource, bytes: &[u8]) -> Result<(), String>;
32
33    /// Upload several resident buffers as one logical staging operation.
34    ///
35    /// Implementers must provide the batching boundary explicitly. A default
36    /// per-buffer loop hides stream synchronization and defeats the resident
37    /// seed-scatter path's upload amortization contract.
38    fn upload_resident_many(&self, uploads: &[(&Self::Resource, &[u8])]) -> Result<(), String>;
39
40    /// Upload byte ranges into resident buffers as one logical staging
41    /// operation.
42    ///
43    /// Backends with true ranged resident uploads should override this method.
44    /// The default only supports zero-offset uploads and forwards them through
45    /// [`Self::upload_resident_many`], preserving existing test dispatchers
46    /// while allowing CUDA-backed paths to avoid padding small seed batches to
47    /// the full retained capacity.
48    fn upload_resident_at_many(
49        &self,
50        uploads: &[(&Self::Resource, usize, &[u8])],
51    ) -> Result<(), String> {
52        if uploads.iter().any(|(_, offset, _)| *offset != 0) {
53            return Err(
54                "weir IFDS resident dispatch does not implement ranged uploads. Fix: provide upload_resident_at_many for this backend or use zero-offset uploads only."
55                    .to_string(),
56            );
57        }
58        let mut zero_offset_uploads = SmallVec::<[(&Self::Resource, &[u8]); 8]>::new();
59        zero_offset_uploads.try_reserve(uploads.len()).map_err(|error| {
60            format!(
61                "weir IFDS resident dispatch could not reserve {} zero-offset upload tuple(s): {error}. Fix: shard the resident upload batch before dispatch.",
62                uploads.len()
63            )
64        })?;
65        for &(resource, _, bytes) in uploads {
66            zero_offset_uploads.push((resource, bytes));
67        }
68        self.upload_resident_many(&zero_offset_uploads)
69    }
70
71    /// Download a resident buffer.
72    fn download_resident(&self, resource: &Self::Resource) -> Result<Vec<u8>, String>;
73
74    /// Download a resident buffer into caller-owned host storage.
75    fn download_resident_into(
76        &self,
77        resource: &Self::Resource,
78        output: &mut Vec<u8>,
79    ) -> Result<(), String>;
80
81    /// Download one byte range from a resident buffer.
82    ///
83    /// Implementers must provide a real ranged transfer. A default full-buffer
84    /// download would turn every convergence poll into an O(frontier) copy and
85    /// hide the exact production regression this resident API exists to avoid.
86    fn download_resident_range(
87        &self,
88        resource: &Self::Resource,
89        byte_offset: usize,
90        byte_len: usize,
91    ) -> Result<Vec<u8>, String>;
92
93    /// Download one byte range from a resident buffer into caller-owned host
94    /// storage.
95    fn download_resident_range_into(
96        &self,
97        resource: &Self::Resource,
98        byte_offset: usize,
99        byte_len: usize,
100        output: &mut Vec<u8>,
101    ) -> Result<(), String>;
102
103    /// Download several resident byte ranges into caller-owned host buffers as
104    /// one logical readback operation.
105    fn download_resident_ranges_into(
106        &self,
107        ranges: &[(&Self::Resource, usize, usize)],
108        outputs: &mut [&mut Vec<u8>],
109    ) -> Result<(), String> {
110        if ranges.len() != outputs.len() {
111            return Err(format!(
112                "weir IFDS resident ranged batch download expected matching range/output counts but got {} range(s) and {} output(s). Fix: pass one caller-owned output Vec per readback range.",
113                ranges.len(),
114                outputs.len()
115            ));
116        }
117        for ((resource, byte_offset, byte_len), output) in ranges.iter().zip(outputs.iter_mut()) {
118            self.download_resident_range_into(resource, *byte_offset, *byte_len, output)?;
119        }
120        Ok(())
121    }
122
123    /// Free a resident buffer.
124    fn free_resident(&self, resource: Self::Resource) -> Result<(), String>;
125
126    /// Dispatch `program` with resident buffers in binding order.
127    fn dispatch_resident(
128        &self,
129        program: &Program,
130        resources: &[Self::Resource],
131        grid_override: Option<[u32; 3]>,
132    ) -> Result<(), String>;
133
134    /// Dispatch an ordered sequence of resident-buffer programs and read
135    /// selected resident byte ranges into caller-owned host buffers.
136    ///
137    /// The default preserves correctness through `dispatch_resident` plus
138    /// `download_resident_ranges_into`. CUDA-backed adapters override this so
139    /// a fixed-point window and final compact readback are enqueued behind one
140    /// device stream fence instead of synchronizing once per iteration.
141    #[allow(clippy::type_complexity)]
142    fn dispatch_resident_sequence_read_ranges_into(
143        &self,
144        steps: &[(&Program, &[Self::Resource], Option<[u32; 3]>)],
145        read_ranges: &[(&Self::Resource, usize, usize)],
146        outputs: &mut [&mut Vec<u8>],
147    ) -> Result<(), String> {
148        for (program, resources, grid_override) in steps {
149            self.dispatch_resident(program, resources, *grid_override)?;
150        }
151        self.download_resident_ranges_into(read_ranges, outputs)
152    }
153
154    /// Dispatch a resident sequence prefix, repeat a resident sub-sequence, and
155    /// read selected resident byte ranges into caller-owned host buffers.
156    ///
157    /// The default materializes no extra host sequence vector; it loops the
158    /// repeated kernel group directly. CUDA-backed adapters can override this
159    /// to forward repeat structure to the backend, avoiding per-iteration
160    /// sequence construction and repeated launch preparation.
161    #[allow(clippy::type_complexity)]
162    fn dispatch_resident_repeated_sequence_read_ranges_into(
163        &self,
164        prefix_steps: &[(&Program, &[Self::Resource], Option<[u32; 3]>)],
165        repeated_steps: &[(&Program, &[Self::Resource], Option<[u32; 3]>)],
166        repeat_count: u32,
167        read_ranges: &[(&Self::Resource, usize, usize)],
168        outputs: &mut [&mut Vec<u8>],
169    ) -> Result<(), String> {
170        for (program, resources, grid_override) in prefix_steps {
171            self.dispatch_resident(program, resources, *grid_override)?;
172        }
173        for _ in 0..repeat_count {
174            for (program, resources, grid_override) in repeated_steps {
175                self.dispatch_resident(program, resources, *grid_override)?;
176            }
177        }
178        self.download_resident_ranges_into(read_ranges, outputs)
179    }
180}