use crate::collectives::sealed::Sealed;
use crate::collectives::Collectives;
use crate::engine::coset::CosetOrder;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ChunkMap {
positions: u32,
coset_dimension: u32,
cosets: u32,
chunks: u32,
#[cfg(feature = "mpi")]
requested: u32,
}
impl ChunkMap {
pub(crate) fn new(positions: usize, coset_dimension: usize, chunks: usize) -> Self {
assert!(
positions.is_power_of_two(),
"ChunkMap: {positions} positions is not a power of two",
);
assert!(chunks > 0, "ChunkMap: a layer needs at least one chunk");
let coset_dimension = coset_dimension as u32;
let positions = positions as u32;
debug_assert!(
(1u32 << coset_dimension) <= positions,
"ChunkMap: a coset of {} buckets does not fit in {positions}",
1u32 << coset_dimension,
);
let cosets = positions >> coset_dimension;
Self {
positions,
coset_dimension,
cosets,
chunks: (chunks as u32).min(cosets).max(1),
#[cfg(feature = "mpi")]
requested: chunks as u32,
}
}
pub(crate) fn of_order(order: &CosetOrder, chunks: usize) -> Self {
Self::new(order.positions(), order.dimension(), chunks)
}
pub(crate) fn positions(&self) -> usize {
self.positions as usize
}
pub(crate) fn chunks(&self) -> usize {
self.chunks as usize
}
#[cfg(feature = "mpi")]
pub(crate) fn requested_chunks(&self) -> u32 {
self.requested
}
pub(crate) fn bound(&self, k: usize) -> u32 {
assert!(k <= self.chunks(), "ChunkMap: chunk {k} is out of range");
let coset = (k as u64 * u64::from(self.cosets)).div_ceil(u64::from(self.chunks)) as u32;
coset << self.coset_dimension
}
pub(crate) fn chunk_of_position(&self, position: u32) -> usize {
let coset = u64::from(position >> self.coset_dimension);
((coset * u64::from(self.chunks)) / u64::from(self.cosets)) as usize
}
}
pub trait Payload: Default + Send + 'static {
fn byte_parts(&self) -> Vec<&[u8]>;
fn receive_into(&mut self, lens: &[usize]) -> Vec<&mut [u8]>;
fn finish_receive(&mut self);
fn early_parts(&self) -> Vec<&[u8]> {
self.byte_parts()
}
fn bulk_parts(&self, map: &ChunkMap) -> Vec<Vec<&[u8]>> {
let _ = map;
Vec::new()
}
fn early_receive_into(&mut self, lens: &[usize]) -> Vec<&mut [u8]> {
self.receive_into(lens)
}
fn bulk_receive_into(&mut self, map: &ChunkMap) -> Vec<Vec<&mut [u8]>> {
let _ = map;
Vec::new()
}
}
pub trait ChunkWait: Sync {
fn wait_chunk(&self, k: usize);
}
pub(super) struct AlreadyHere;
impl ChunkWait for AlreadyHere {
fn wait_chunk(&self, _k: usize) {}
}
pub trait Transport: Collectives {
fn exchange_layer<P, F, R>(
&self,
send: Vec<Option<P>>,
spare: &mut Vec<P>,
map: &ChunkMap,
body: F,
) -> (Vec<Option<P>>, R)
where
P: Payload,
F: FnOnce(&[Option<P>], &dyn ChunkWait) -> R;
fn exchange<P: Payload>(&self, send: Vec<Option<P>>, spare: &mut Vec<P>) -> Vec<Option<P>> {
self.exchange_layer(send, spare, &ChunkMap::default(), |_, _| ()).0
}
fn gather_to_root(&self, parts: Vec<&[u8]>) -> Option<Vec<Vec<Vec<u8>>>> {
let size = self.size() as usize;
let this_rank = self.rank() as usize;
let mine = ByteParts(parts.iter().map(|p| p.to_vec()).collect());
if this_rank != ROOT {
let mut send: Vec<Option<ByteParts>> = (0..size).map(|_| None).collect();
send[ROOT] = Some(mine);
self.exchange(send, &mut Vec::new());
return None;
}
let mut received = self
.exchange((0..size).map(|_| None).collect::<Vec<Option<ByteParts>>>(), &mut Vec::new());
let mut mine = Some(mine);
Some(
(0..size)
.map(|q| {
let got = if q == ROOT { mine.take() } else { received[q].take() };
got.unwrap_or_else(|| {
panic!(
"gather_to_root: partition {q} sent nothing (it must send its \
parts, empty or not)"
)
})
.0
})
.collect(),
)
}
}
pub(crate) struct SoloTransport;
impl Sealed for SoloTransport {}
impl Collectives for SoloTransport {
fn rank(&self) -> u32 {
0
}
fn size(&self) -> u32 {
1
}
fn allreduce_max_u8(&self, v: u8) -> u8 {
v
}
fn allreduce_sum_u64(&self, _buffer: &mut [u64]) {}
fn allreduce_sum_f64(&self, _buffer: &mut [f64]) {}
fn barrier(&self) {}
}
impl Transport for SoloTransport {
fn exchange_layer<P, F, R>(
&self,
send: Vec<Option<P>>,
_spare: &mut Vec<P>,
_map: &ChunkMap,
body: F,
) -> (Vec<Option<P>>, R)
where
P: Payload,
F: FnOnce(&[Option<P>], &dyn ChunkWait) -> R,
{
debug_assert!(send.iter().all(Option::is_none), "a group of one sends to no one");
let result = body(&send, &AlreadyHere);
(send, result)
}
}
pub(super) const ROOT: usize = 0;
#[derive(Default)]
struct ByteParts(Vec<Vec<u8>>);
impl Payload for ByteParts {
fn byte_parts(&self) -> Vec<&[u8]> {
self.0.iter().map(Vec::as_slice).collect()
}
fn receive_into(&mut self, lens: &[usize]) -> Vec<&mut [u8]> {
self.0.resize_with(lens.len(), Vec::new);
for (part, &len) in self.0.iter_mut().zip(lens) {
part.clear();
part.resize(len, 0);
}
self.0.iter_mut().map(Vec::as_mut_slice).collect()
}
fn finish_receive(&mut self) {}
}
#[cfg(test)]
mod tests;