rlx_runtime/dist/
inference.rs1use super::{WeightCache, WeightRef, resolve_device};
6use crate::compiled::CompiledGraph;
7use crate::{Device, Session};
8use rlx_driver::ProcessGroup;
9use rlx_ir::Graph;
10use serde::{Deserialize, Serialize};
11
12const TAG_SPEC: u32 = 40;
15const TAG_ACT: u32 = 41;
16
17#[derive(Serialize, Deserialize, Clone, Debug)]
20pub struct StageSpec {
21 pub graph: Graph,
22 pub input: String,
24 pub output: String,
26 pub weights: Vec<WeightRef>,
27 pub device: String,
30}
31
32impl StageSpec {
33 pub fn new(
35 graph: Graph,
36 input: impl Into<String>,
37 output: impl Into<String>,
38 device: impl Into<String>,
39 ) -> Self {
40 Self {
41 graph,
42 input: input.into(),
43 output: output.into(),
44 weights: Vec::new(),
45 device: device.into(),
46 }
47 }
48 pub fn weight(mut self, name: impl Into<String>, uri: impl Into<String>) -> Self {
50 self.weights.push(WeightRef {
51 name: name.into(),
52 uri: uri.into(),
53 packed: false,
54 });
55 self
56 }
57 pub fn weight_packed(mut self, name: impl Into<String>, uri: impl Into<String>) -> Self {
60 self.weights.push(WeightRef {
61 name: name.into(),
62 uri: uri.into(),
63 packed: true,
64 });
65 self
66 }
67}
68
69pub fn ship_stage(group: &ProcessGroup, rank: u32, spec: &StageSpec) -> Result<(), String> {
71 let bytes = serde_json::to_vec(spec).map_err(|e| e.to_string())?;
72 group
73 .transport()
74 .send_bytes(rank, TAG_SPEC, &bytes)
75 .map_err(|e| format!("ship_stage: {e}"))
76}
77
78pub fn send_activation(group: &ProcessGroup, to: u32, data: &[f32]) -> Result<(), String> {
80 group.send_f32(to, TAG_ACT, data).map_err(|e| e.to_string())
81}
82
83pub fn recv_activation(group: &ProcessGroup, from: u32) -> Result<Vec<f32>, String> {
85 group.recv_f32(from, TAG_ACT).map_err(|e| e.to_string())
86}
87
88pub struct WorkerStage {
90 pub device: Device,
92 pub nodes: usize,
94 input: String,
95 compiled: CompiledGraph,
96}
97
98impl WorkerStage {
99 pub fn run(&mut self, x: &[f32]) -> Vec<f32> {
101 self.compiled
102 .run(&[(self.input.as_str(), x)])
103 .into_iter()
104 .next()
105 .unwrap_or_default()
106 }
107 pub fn input_name(&self) -> &str {
108 &self.input
109 }
110}
111
112pub fn recv_stage<F>(group: &ProcessGroup, mut fallback: F) -> Result<WorkerStage, String>
120where
121 F: FnMut(&str) -> Vec<f32>,
122{
123 let bytes = group
124 .transport()
125 .recv_bytes(0, TAG_SPEC)
126 .map_err(|e| format!("recv_stage: {e}"))?;
127 let spec: StageSpec = serde_json::from_slice(&bytes).map_err(|e| format!("StageSpec: {e}"))?;
128 let device = resolve_device(&spec.device);
129 let nodes = spec.graph.len();
130 let mut compiled = Session::new(device).compile(spec.graph);
131 let mut cache = WeightCache::new();
134 for w in &spec.weights {
135 if w.packed {
136 let bytes = cache
139 .bytes(&w.uri)
140 .map_err(|e| format!("weight {}: {e}", w.name))?;
141 compiled.set_param_typed(&w.name, &bytes, rlx_ir::DType::U8);
142 } else {
143 let vals = cache.f32(&w.uri).unwrap_or_else(|_| fallback(&w.uri));
146 compiled.set_param(&w.name, &vals);
147 }
148 }
149 Ok(WorkerStage {
150 device,
151 nodes,
152 input: spec.input,
153 compiled,
154 })
155}
156
157pub fn serve_stage<F>(group: &ProcessGroup, resolve: F) -> Result<Device, String>
163where
164 F: FnMut(&str) -> Vec<f32>,
165{
166 let n = group.world_size();
167 let rank = group.rank();
168 let mut stage = recv_stage(group, resolve)?;
169 let input = recv_activation(group, rank - 1)?;
170 let out = stage.run(&input);
171 let next = if rank + 1 < n { rank + 1 } else { 0 };
172 send_activation(group, next, &out)?;
173 Ok(stage.device)
174}
175
176pub fn serve_stage_uri(group: &ProcessGroup) -> Result<Device, String> {
180 serve_stage(group, |uri| {
181 eprintln!("rlx dist: no resolver for weight URI [{uri}]");
182 Vec::new()
183 })
184}