paulistrings/engine/
transport.rs1use crate::collectives::sealed::Sealed;
4use crate::collectives::Collectives;
5use crate::engine::coset::CosetOrder;
6
7#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11pub struct ChunkMap {
12 positions: u32,
13 coset_dimension: u32,
15 cosets: u32,
16 chunks: u32,
18 #[cfg(feature = "mpi")]
20 requested: u32,
21}
22
23impl ChunkMap {
24 pub(crate) fn new(positions: usize, coset_dimension: usize, chunks: usize) -> Self {
26 assert!(
27 positions.is_power_of_two(),
28 "ChunkMap: {positions} positions is not a power of two",
29 );
30 assert!(chunks > 0, "ChunkMap: a layer needs at least one chunk");
31 let coset_dimension = coset_dimension as u32;
32 let positions = positions as u32;
33 debug_assert!(
34 (1u32 << coset_dimension) <= positions,
35 "ChunkMap: a coset of {} buckets does not fit in {positions}",
36 1u32 << coset_dimension,
37 );
38 let cosets = positions >> coset_dimension;
39 Self {
40 positions,
41 coset_dimension,
42 cosets,
43 chunks: (chunks as u32).min(cosets).max(1),
44 #[cfg(feature = "mpi")]
45 requested: chunks as u32,
46 }
47 }
48
49 pub(crate) fn of_order(order: &CosetOrder, chunks: usize) -> Self {
51 Self::new(order.positions(), order.dimension(), chunks)
52 }
53
54 pub(crate) fn positions(&self) -> usize {
56 self.positions as usize
57 }
58
59 pub(crate) fn chunks(&self) -> usize {
60 self.chunks as usize
61 }
62
63 #[cfg(feature = "mpi")]
65 pub(crate) fn requested_chunks(&self) -> u32 {
66 self.requested
67 }
68
69 pub(crate) fn bound(&self, k: usize) -> u32 {
71 assert!(k <= self.chunks(), "ChunkMap: chunk {k} is out of range");
72 let coset = (k as u64 * u64::from(self.cosets)).div_ceil(u64::from(self.chunks)) as u32;
73 coset << self.coset_dimension
74 }
75
76 pub(crate) fn chunk_of_position(&self, position: u32) -> usize {
78 let coset = u64::from(position >> self.coset_dimension);
79 ((coset * u64::from(self.chunks)) / u64::from(self.cosets)) as usize
80 }
81}
82
83pub trait Payload: Default + Send + 'static {
87 fn byte_parts(&self) -> Vec<&[u8]>;
89
90 fn receive_into(&mut self, lens: &[usize]) -> Vec<&mut [u8]>;
94
95 fn finish_receive(&mut self);
97
98 fn early_parts(&self) -> Vec<&[u8]> {
100 self.byte_parts()
101 }
102
103 fn bulk_parts(&self, map: &ChunkMap) -> Vec<Vec<&[u8]>> {
107 let _ = map;
108 Vec::new()
109 }
110
111 fn early_receive_into(&mut self, lens: &[usize]) -> Vec<&mut [u8]> {
113 self.receive_into(lens)
114 }
115
116 fn bulk_receive_into(&mut self, map: &ChunkMap) -> Vec<Vec<&mut [u8]>> {
118 let _ = map;
119 Vec::new()
120 }
121}
122
123pub trait ChunkWait: Sync {
127 fn wait_chunk(&self, k: usize);
129}
130
131pub(super) struct AlreadyHere;
133
134impl ChunkWait for AlreadyHere {
135 fn wait_chunk(&self, _k: usize) {}
136}
137
138pub trait Transport: Collectives {
142 fn exchange_layer<P, F, R>(
148 &self,
149 send: Vec<Option<P>>,
150 spare: &mut Vec<P>,
151 map: &ChunkMap,
152 body: F,
153 ) -> (Vec<Option<P>>, R)
154 where
155 P: Payload,
156 F: FnOnce(&[Option<P>], &dyn ChunkWait) -> R;
157
158 fn exchange<P: Payload>(&self, send: Vec<Option<P>>, spare: &mut Vec<P>) -> Vec<Option<P>> {
160 self.exchange_layer(send, spare, &ChunkMap::default(), |_, _| ()).0
161 }
162
163 fn gather_to_root(&self, parts: Vec<&[u8]>) -> Option<Vec<Vec<Vec<u8>>>> {
167 let size = self.size() as usize;
168 let this_rank = self.rank() as usize;
169 let mine = ByteParts(parts.iter().map(|p| p.to_vec()).collect());
170
171 if this_rank != ROOT {
172 let mut send: Vec<Option<ByteParts>> = (0..size).map(|_| None).collect();
173 send[ROOT] = Some(mine);
174 self.exchange(send, &mut Vec::new());
175 return None;
176 }
177
178 let mut received = self
179 .exchange((0..size).map(|_| None).collect::<Vec<Option<ByteParts>>>(), &mut Vec::new());
180 let mut mine = Some(mine);
181 Some(
182 (0..size)
183 .map(|q| {
184 let got = if q == ROOT { mine.take() } else { received[q].take() };
185 got.unwrap_or_else(|| {
186 panic!(
187 "gather_to_root: partition {q} sent nothing (it must send its \
188 parts, empty or not)"
189 )
190 })
191 .0
192 })
193 .collect(),
194 )
195 }
196}
197
198pub(crate) struct SoloTransport;
200
201impl Sealed for SoloTransport {}
202
203impl Collectives for SoloTransport {
204 fn rank(&self) -> u32 {
205 0
206 }
207 fn size(&self) -> u32 {
208 1
209 }
210 fn allreduce_max_u8(&self, v: u8) -> u8 {
211 v
212 }
213 fn allreduce_sum_u64(&self, _buffer: &mut [u64]) {}
214 fn allreduce_sum_f64(&self, _buffer: &mut [f64]) {}
215 fn barrier(&self) {}
216}
217
218impl Transport for SoloTransport {
219 fn exchange_layer<P, F, R>(
220 &self,
221 send: Vec<Option<P>>,
222 _spare: &mut Vec<P>,
223 _map: &ChunkMap,
224 body: F,
225 ) -> (Vec<Option<P>>, R)
226 where
227 P: Payload,
228 F: FnOnce(&[Option<P>], &dyn ChunkWait) -> R,
229 {
230 debug_assert!(send.iter().all(Option::is_none), "a group of one sends to no one");
231 let result = body(&send, &AlreadyHere);
232 (send, result)
233 }
234}
235
236pub(super) const ROOT: usize = 0;
238
239#[derive(Default)]
241struct ByteParts(Vec<Vec<u8>>);
242
243impl Payload for ByteParts {
244 fn byte_parts(&self) -> Vec<&[u8]> {
245 self.0.iter().map(Vec::as_slice).collect()
246 }
247
248 fn receive_into(&mut self, lens: &[usize]) -> Vec<&mut [u8]> {
249 self.0.resize_with(lens.len(), Vec::new);
250 for (part, &len) in self.0.iter_mut().zip(lens) {
251 part.clear();
252 part.resize(len, 0);
253 }
254 self.0.iter_mut().map(Vec::as_mut_slice).collect()
255 }
256
257 fn finish_receive(&mut self) {}
258}
259
260#[cfg(test)]
261mod tests;