paulistrings/
collectives.rs

1//! [`Collectives`], the group operations a partition's truncation and layer loop issue besides the exchange.
2
3/// Seals [`Collectives`] and, through it, [`Transport`](crate::Transport).
4pub(crate) mod sealed {
5    pub trait Sealed {}
6}
7
8/// The group operations a partition needs besides the exchange: its rank, the reductions, and a barrier.
9///
10/// Every partition must issue the identical sequence of collective and transport calls, and every reduction must return the identical value on every partition.
11/// Sealed: implemented only by the in-process, solo and `mpi::MpiTransport` transports.
12pub trait Collectives: sealed::Sealed + Send + Sync {
13    /// This partition's index in the group, `0 <= rank < size`.
14    fn rank(&self) -> u32;
15    /// Number of partitions in the group.
16    fn size(&self) -> u32;
17    /// Maximum of `v` over the group.
18    fn allreduce_max_u8(&self, v: u8) -> u8;
19    /// Element-wise wrapping sum of `buffer` over the group, in place; every partition passes the same length.
20    fn allreduce_sum_u64(&self, buffer: &mut [u64]);
21    /// Element-wise sum of `buffer` over the group, in place, bitwise identical on every partition.
22    ///
23    /// The combination order is the implementation's, so the result may differ by rounding from a serial sum; a slot only one partition fills is exact.
24    fn allreduce_sum_f64(&self, buffer: &mut [f64]);
25    /// Block until every partition has arrived.
26    fn barrier(&self);
27
28    /// Panic unless every partition passed the same `fingerprint`; one collective.
29    fn check_consistency(&self, fingerprint: u64) {
30        // Per bit, how many partitions set it: an agreeing group answers 0 or `size` for every bit.
31        let mut counts = [0u64; 64];
32        for (i, count) in counts.iter_mut().enumerate() {
33            *count = (fingerprint >> i) & 1;
34        }
35        self.allreduce_sum_u64(&mut counts);
36        let size = u64::from(self.size());
37        for (bit, &count) in counts.iter().enumerate() {
38            assert!(
39                count == 0 || count == size,
40                "the partitions disagree about the run: partition {} offered fingerprint \
41                 {fingerprint:#018x}, and {count} of {size} partitions set bit {bit} of theirs. \
42                 Every partition must be driven through the same circuit, in the same direction, \
43                 under the same policy and options — a group that is not stays in step only by \
44                 luck.",
45                self.rank(),
46            );
47        }
48    }
49}