1use crate::backend::ExecutableGraph;
19use rlx_driver::Device;
20
21pub struct CompiledGraph {
28 inner: Box<dyn ExecutableGraph>,
29 device: Device,
30}
31
32impl Clone for CompiledGraph {
33 fn clone(&self) -> Self {
36 Self {
37 inner: self.inner.clone_box(),
38 device: self.device,
39 }
40 }
41}
42
43impl CompiledGraph {
44 pub(crate) fn new(inner: Box<dyn ExecutableGraph>, device: Device) -> Self {
45 Self { inner, device }
46 }
47
48 pub fn device(&self) -> Device {
50 self.device
51 }
52
53 pub fn set_param(&mut self, name: &str, data: &[f32]) {
56 self.inner.set_param(name, data);
57 }
58
59 pub fn run(&mut self, inputs: &[(&str, &[f32])]) -> Vec<Vec<f32>> {
62 self.inner.run(inputs)
63 }
64
65 pub fn run_read_outputs(
67 &mut self,
68 inputs: &[(&str, &[f32])],
69 read_indices: Option<&[usize]>,
70 ) -> Vec<Vec<f32>> {
71 self.inner.run_read_outputs(inputs, read_indices)
72 }
73
74 pub fn read_output_row(
76 &self,
77 out_idx: usize,
78 row: usize,
79 row_inner: usize,
80 ) -> Option<Vec<f32>> {
81 self.inner.read_output_row(out_idx, row, row_inner)
82 }
83
84 pub fn run_raw(&mut self, inputs: &[(&str, &[f32])]) -> Vec<(*const f32, usize)> {
91 self.inner.run_raw(inputs)
92 }
93
94 pub fn run_slots(&mut self, inputs: &[&[f32]]) -> &[(usize, usize)] {
98 self.inner.run_slots(inputs)
99 }
100
101 pub fn arena_ptr(&self) -> *const u8 {
103 self.inner.arena_ptr()
104 }
105
106 pub fn bind_handle(&mut self, name: &str, data: &[f32]) -> bool {
111 self.inner.bind_handle(name, data)
112 }
113
114 pub fn read_handle(&self, name: &str) -> Option<Vec<f32>> {
116 self.inner.read_handle(name)
117 }
118
119 pub fn bind_gpu_handle(&mut self, name: &str, data: &[f32]) -> bool {
121 self.inner.bind_gpu_handle(name, data)
122 }
123
124 pub fn has_gpu_handle(&self, name: &str) -> bool {
125 self.inner.has_gpu_handle(name)
126 }
127
128 pub fn set_gpu_handle_feed(&mut self, handle_name: &str, output_index: usize) -> bool {
129 self.inner.set_gpu_handle_feed(handle_name, output_index)
130 }
131
132 pub fn read_gpu_handle(&self, name: &str) -> Option<Vec<f32>> {
133 self.inner.read_gpu_handle(name)
134 }
135
136 pub fn read_gpu_handle_row(
138 &self,
139 name: &str,
140 row: usize,
141 row_inner: usize,
142 ) -> Option<Vec<f32>> {
143 self.inner.read_gpu_handle_row(name, row, row_inner)
144 }
145
146 pub fn register_kv_row_feed(&mut self, handle_name: &str, output_index: usize) -> bool {
150 self.inner.register_kv_row_feed(handle_name, output_index)
151 }
152
153 pub fn feed_kv_row(&mut self, src_row: usize, dst_row: usize, row_elems: usize) -> bool {
157 self.inner.feed_kv_row(src_row, dst_row, row_elems)
158 }
159
160 pub fn prepare_resident_gpu_handle(&mut self, name: &str) -> bool {
162 self.inner.prepare_resident_gpu_handle(name)
163 }
164
165 pub fn stage_bound_gpu_handles_to_arena(&mut self) -> bool {
167 self.inner.stage_bound_gpu_handles_to_arena();
168 true
169 }
170
171 pub fn seed_resident_kv_prefix_from(
173 &mut self,
174 src: &CompiledGraph,
175 prefix_tokens: usize,
176 outgoing_upper: usize,
177 kv_dim: usize,
178 n_layers: usize,
179 ) -> bool {
180 if self.device != src.device {
181 return false;
182 }
183 self.inner.seed_resident_kv_prefix_from(
184 src.inner.as_ref(),
185 prefix_tokens,
186 outgoing_upper,
187 kv_dim,
188 n_layers,
189 )
190 }
191
192 pub fn copy_resident_kv_rows_from(
194 &mut self,
195 src: &CompiledGraph,
196 from_row: usize,
197 to_row: usize,
198 outgoing_upper: usize,
199 kv_dim: usize,
200 n_layers: usize,
201 ) -> bool {
202 if self.device != src.device {
203 return false;
204 }
205 self.inner.copy_resident_kv_rows_from(
206 src.inner.as_ref(),
207 from_row,
208 to_row,
209 outgoing_upper,
210 kv_dim,
211 n_layers,
212 )
213 }
214
215 pub fn copy_params_from(&mut self, src: &CompiledGraph) -> bool {
217 if self.device != src.device {
218 return false;
219 }
220 self.inner.copy_params_from(src.inner.as_ref())
221 }
222
223 pub fn run_feed_gpu_handle(
225 &mut self,
226 inputs: &[(&str, &[f32])],
227 handle_name: &str,
228 output_index: usize,
229 ) -> Option<Vec<f32>> {
230 self.inner
231 .run_feed_gpu_handle(inputs, handle_name, output_index)
232 }
233
234 pub fn set_active_extent(&mut self, extent: Option<(usize, usize)>) {
241 #[cfg(feature = "cpu")]
242 if let Some((actual, _)) = extent {
243 crate::onnx_active::set_active_token_count(Some(actual))
244 }
245 self.inner.set_active_extent(extent);
246 }
247
248 pub fn set_moe_resident_experts(&mut self, mask: &[bool]) {
250 self.inner.set_moe_resident_experts(mask);
251 }
252
253 pub fn set_moe_resident_experts_per_layer(&mut self, masks: &[&[bool]]) {
255 self.inner.set_moe_resident_experts_per_layer(masks);
256 }
257
258 pub fn enable_moe_topk_capture(&mut self, num_experts: usize) -> bool {
260 self.inner.enable_moe_topk_capture(num_experts)
261 }
262
263 pub fn take_moe_topk_capture(&mut self) -> Option<Vec<Vec<u32>>> {
265 self.inner.take_moe_topk_capture()
266 }
267
268 pub fn take_moe_residency_stats(&mut self) -> Option<crate::MoeResidencyStats> {
270 self.inner.take_moe_residency_stats()
271 }
272
273 pub fn commit_no_wait(&mut self, inputs: &[(&str, &[f32])]) {
281 self.inner.commit_no_wait(inputs);
282 }
283
284 pub fn sync_pending(&mut self) {
286 self.inner.sync_pending();
287 }
288
289 pub fn run_pipelined(&mut self, input_sets: &[Vec<(&str, &[f32])>]) -> Vec<Vec<Vec<f32>>> {
295 self.inner.run_pipelined(input_sets)
296 }
297
298 pub fn set_param_typed(&mut self, name: &str, data: &[u8], dtype: rlx_ir::DType) {
303 self.inner.set_param_typed(name, data, dtype);
304 }
305
306 pub fn finalize_params(&mut self) {
308 self.inner.finalize_params();
309 }
310
311 pub fn run_typed(
316 &mut self,
317 inputs: &[(&str, &[u8], rlx_ir::DType)],
318 ) -> Vec<(Vec<u8>, rlx_ir::DType)> {
319 self.inner.run_typed(inputs)
320 }
321
322 pub fn set_rng(&mut self, rng: rlx_ir::RngOptions) {
324 self.inner.set_rng(rng);
325 }
326
327 pub fn rng(&self) -> rlx_ir::RngOptions {
329 self.inner.rng()
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use crate::*;
336
337 #[test]
338 #[cfg(feature = "cpu")]
339 fn end_to_end_session() {
340 let mut g = Graph::new("matmul_bias_gelu");
341 let x = g.input("x", Shape::new(&[2, 4], DType::F32));
342 let w = g.param("w", Shape::new(&[4, 3], DType::F32));
343 let b = g.param("b", Shape::new(&[3], DType::F32));
344 let mm = g.matmul(x, w, Shape::new(&[2, 3], DType::F32));
345 let add = g.binary(op::BinaryOp::Add, mm, b, Shape::new(&[2, 3], DType::F32));
346 let out = g.activation(op::Activation::Gelu, add, Shape::new(&[2, 3], DType::F32));
347 g.set_outputs(vec![out]);
348
349 let session = Session::new(Device::Cpu);
351 let mut compiled = session.compile(g);
352
353 compiled.set_param(
356 "w",
357 &[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0],
358 );
359 compiled.set_param("b", &[0.5, -0.5, 0.0]);
360
361 let x_data = vec![
363 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, ];
366 let outputs = compiled.run(&[("x", &x_data)]);
367
368 assert_eq!(outputs.len(), 1);
369 let result = &outputs[0];
370 assert_eq!(result.len(), 6); assert!(
374 (result[0] - 1.399).abs() < 0.01,
375 "gelu(1.5) = {}",
376 result[0]
377 );
378 assert!(
379 (result[1] - -0.154).abs() < 0.01,
380 "gelu(-0.5) = {}",
381 result[1]
382 );
383 assert!((result[2]).abs() < 0.01, "gelu(0) = {}", result[2]);
384
385 assert!(
387 (result[3] - 0.346).abs() < 0.01,
388 "gelu(0.5) = {}",
389 result[3]
390 );
391 assert!(
392 (result[4] - 0.346).abs() < 0.01,
393 "gelu(0.5) = {}",
394 result[4]
395 );
396
397 let x2 = vec![0.0f32; 8];
399 let outputs2 = compiled.run(&[("x", &x2)]);
400 let r2 = &outputs2[0];
402 assert!((r2[0] - 0.346).abs() < 0.01, "gelu(0.5) = {}", r2[0]); }
404
405 #[test]
406 #[cfg(feature = "cpu")]
407 fn device_display() {
408 use crate::device_ext::is_available;
409 assert!(format!("{}", Device::Cpu).starts_with("CPU"));
410 assert!(is_available(Device::Cpu));
411 #[cfg(not(feature = "gpu"))]
414 assert!(!is_available(Device::Gpu));
415 #[cfg(not(feature = "cuda"))]
416 assert!(!is_available(Device::Cuda));
417 #[cfg(not(feature = "rocm"))]
418 assert!(!is_available(Device::Rocm));
419 #[cfg(not(feature = "tpu"))]
420 assert!(!is_available(Device::Tpu));
421 }
422}