1use std::io;
13use std::ptr;
14
15use crate::datatype::{
16 ids, Buffer, BufferMut, Equivalence, PartitionedBuffer, PartitionedBufferMut,
17};
18use crate::point_to_point::{vec_from_bytes, Process};
19use crate::request::{Request, Scope, StaticScope};
20use crate::topology::{CommData, Communicator};
21use crate::transport;
22use crate::{Rank, Tag};
23
24const T_BCAST: Tag = 3;
26const T_GATHER: Tag = 4;
27const T_SCATTER: Tag = 5;
28const T_ALLGATHER_UP: Tag = 6;
29const T_ALLTOALL: Tag = 8;
30const T_REDUCE: Tag = 9;
31const T_SCAN: Tag = 11;
32const T_EXSCAN: Tag = 12;
33const T_ALLGATHERV_UP: Tag = 13;
34const T_ALLGATHERV_DOWN: Tag = 14;
35const T_GATHERV: Tag = 15;
36const T_BARRIER_ROUND: Tag = 200;
38
39#[inline]
41fn real_rank(rel: Rank, root: Rank, n: Rank) -> Rank {
42 (rel + root) % n
43}
44const T_SCATTERV: Tag = 16;
45const T_ALLTOALLV: Tag = 17;
46
47const DT_BYTES: u32 = ids::U8;
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq)]
55enum OpKind {
56 Sum,
57 Product,
58 Max,
59 Min,
60 LogicalAnd,
61 LogicalOr,
62 LogicalXor,
63 BitwiseAnd,
64 BitwiseOr,
65 BitwiseXor,
66}
67
68pub trait Operation {
71 #[doc(hidden)]
74 fn reduce_bytes(&self, dt_id: u32, acc: &mut [u8], val: &[u8]);
75
76 fn is_commutative(&self) -> bool {
78 true
79 }
80}
81
82#[derive(Copy, Clone, Debug, PartialEq, Eq)]
85pub struct SystemOperation {
86 kind: OpKind,
87}
88
89impl SystemOperation {
90 pub fn sum() -> SystemOperation {
92 SystemOperation { kind: OpKind::Sum }
93 }
94 pub fn product() -> SystemOperation {
96 SystemOperation {
97 kind: OpKind::Product,
98 }
99 }
100 pub fn max() -> SystemOperation {
102 SystemOperation { kind: OpKind::Max }
103 }
104 pub fn min() -> SystemOperation {
106 SystemOperation { kind: OpKind::Min }
107 }
108 pub fn logical_and() -> SystemOperation {
110 SystemOperation {
111 kind: OpKind::LogicalAnd,
112 }
113 }
114 pub fn logical_or() -> SystemOperation {
116 SystemOperation {
117 kind: OpKind::LogicalOr,
118 }
119 }
120 pub fn logical_xor() -> SystemOperation {
122 SystemOperation {
123 kind: OpKind::LogicalXor,
124 }
125 }
126 pub fn bitwise_and() -> SystemOperation {
128 SystemOperation {
129 kind: OpKind::BitwiseAnd,
130 }
131 }
132 pub fn bitwise_or() -> SystemOperation {
134 SystemOperation {
135 kind: OpKind::BitwiseOr,
136 }
137 }
138 pub fn bitwise_xor() -> SystemOperation {
140 SystemOperation {
141 kind: OpKind::BitwiseXor,
142 }
143 }
144
145 pub(crate) fn to_code(self) -> u8 {
147 match self.kind {
148 OpKind::Sum => 0,
149 OpKind::Product => 1,
150 OpKind::Max => 2,
151 OpKind::Min => 3,
152 OpKind::LogicalAnd => 4,
153 OpKind::LogicalOr => 5,
154 OpKind::LogicalXor => 6,
155 OpKind::BitwiseAnd => 7,
156 OpKind::BitwiseOr => 8,
157 OpKind::BitwiseXor => 9,
158 }
159 }
160
161 pub(crate) fn from_code(code: u8) -> SystemOperation {
165 let kind = match code {
166 0 => OpKind::Sum,
167 1 => OpKind::Product,
168 2 => OpKind::Max,
169 3 => OpKind::Min,
170 4 => OpKind::LogicalAnd,
171 5 => OpKind::LogicalOr,
172 6 => OpKind::LogicalXor,
173 7 => OpKind::BitwiseAnd,
174 8 => OpKind::BitwiseOr,
175 _ => OpKind::BitwiseXor,
176 };
177 SystemOperation { kind }
178 }
179}
180
181fn combine<T: Copy>(acc: &mut [u8], val: &[u8], f: impl Fn(T, T) -> T) {
184 let size = std::mem::size_of::<T>();
185 if size == 0 {
186 return;
187 }
188 let n = acc.len() / size;
189 for i in 0..n {
190 unsafe {
192 let ap = acc.as_mut_ptr().add(i * size) as *mut T;
193 let bp = val.as_ptr().add(i * size) as *const T;
194 let a = ptr::read_unaligned(ap);
195 let b = ptr::read_unaligned(bp);
196 ptr::write_unaligned(ap, f(a, b));
197 }
198 }
199}
200
201macro_rules! int_reduce {
202 ($kind:expr, $acc:expr, $val:expr, $t:ty) => {{
203 match $kind {
204 OpKind::Sum => combine::<$t>($acc, $val, |a, b| a.wrapping_add(b)),
205 OpKind::Product => combine::<$t>($acc, $val, |a, b| a.wrapping_mul(b)),
206 OpKind::Max => combine::<$t>($acc, $val, |a, b| if a >= b { a } else { b }),
207 OpKind::Min => combine::<$t>($acc, $val, |a, b| if a <= b { a } else { b }),
208 OpKind::LogicalAnd => combine::<$t>($acc, $val, |a, b| ((a != 0) && (b != 0)) as $t),
209 OpKind::LogicalOr => combine::<$t>($acc, $val, |a, b| ((a != 0) || (b != 0)) as $t),
210 OpKind::LogicalXor => combine::<$t>($acc, $val, |a, b| ((a != 0) ^ (b != 0)) as $t),
211 OpKind::BitwiseAnd => combine::<$t>($acc, $val, |a, b| a & b),
212 OpKind::BitwiseOr => combine::<$t>($acc, $val, |a, b| a | b),
213 OpKind::BitwiseXor => combine::<$t>($acc, $val, |a, b| a ^ b),
214 }
215 }};
216}
217
218macro_rules! float_reduce {
219 ($kind:expr, $acc:expr, $val:expr, $t:ty) => {{
220 match $kind {
221 OpKind::Sum => combine::<$t>($acc, $val, |a, b| a + b),
222 OpKind::Product => combine::<$t>($acc, $val, |a, b| a * b),
223 OpKind::Max => combine::<$t>($acc, $val, |a, b| a.max(b)),
224 OpKind::Min => combine::<$t>($acc, $val, |a, b| a.min(b)),
225 other => panic!(
226 "operation {:?} is not defined for floating-point data",
227 other
228 ),
229 }
230 }};
231}
232
233impl Operation for SystemOperation {
234 fn reduce_bytes(&self, dt_id: u32, acc: &mut [u8], val: &[u8]) {
235 debug_assert_eq!(acc.len(), val.len());
236 match dt_id {
237 ids::I8 => int_reduce!(self.kind, acc, val, i8),
238 ids::U8 => int_reduce!(self.kind, acc, val, u8),
239 ids::I16 => int_reduce!(self.kind, acc, val, i16),
240 ids::U16 => int_reduce!(self.kind, acc, val, u16),
241 ids::I32 => int_reduce!(self.kind, acc, val, i32),
242 ids::U32 => int_reduce!(self.kind, acc, val, u32),
243 ids::I64 => int_reduce!(self.kind, acc, val, i64),
244 ids::U64 => int_reduce!(self.kind, acc, val, u64),
245 ids::ISIZE => int_reduce!(self.kind, acc, val, isize),
246 ids::USIZE => int_reduce!(self.kind, acc, val, usize),
247 ids::I128 => int_reduce!(self.kind, acc, val, i128),
248 ids::U128 => int_reduce!(self.kind, acc, val, u128),
249 ids::F32 => float_reduce!(self.kind, acc, val, f32),
250 ids::F64 => float_reduce!(self.kind, acc, val, f64),
251 ids::BOOL => match self.kind {
252 OpKind::LogicalAnd | OpKind::BitwiseAnd | OpKind::Min => {
253 combine::<u8>(acc, val, |a, b| ((a != 0) && (b != 0)) as u8)
254 }
255 OpKind::LogicalOr | OpKind::BitwiseOr | OpKind::Max => {
256 combine::<u8>(acc, val, |a, b| ((a != 0) || (b != 0)) as u8)
257 }
258 OpKind::LogicalXor | OpKind::BitwiseXor => {
259 combine::<u8>(acc, val, |a, b| ((a != 0) ^ (b != 0)) as u8)
260 }
261 other => panic!("operation {:?} is not defined for bool", other),
262 },
263 other => panic!("reduction not supported for datatype id {other}"),
264 }
265 }
266}
267
268pub struct UserOperation<T: Equivalence> {
272 #[allow(clippy::type_complexity)]
273 f: Box<dyn Fn(&[T], &mut [T]) + Send + Sync>,
274 commutative: bool,
275}
276
277impl<T: Equivalence> UserOperation<T> {
278 pub fn commutative<F>(f: F) -> UserOperation<T>
280 where
281 F: Fn(&[T], &mut [T]) + Send + Sync + 'static,
282 {
283 UserOperation {
284 f: Box::new(f),
285 commutative: true,
286 }
287 }
288
289 pub fn non_commutative<F>(f: F) -> UserOperation<T>
291 where
292 F: Fn(&[T], &mut [T]) + Send + Sync + 'static,
293 {
294 UserOperation {
295 f: Box::new(f),
296 commutative: false,
297 }
298 }
299}
300
301impl<T: Equivalence> Operation for UserOperation<T> {
302 fn reduce_bytes(&self, _dt_id: u32, acc: &mut [u8], val: &[u8]) {
303 let mut a: Vec<T> = vec_from_bytes::<T>(acc);
304 let b: Vec<T> = vec_from_bytes::<T>(val);
305 (self.f)(&b, &mut a);
306 let size = std::mem::size_of::<T>();
308 let a_bytes =
310 unsafe { std::slice::from_raw_parts(a.as_ptr() as *const u8, a.len() * size) };
311 acc[..a_bytes.len()].copy_from_slice(a_bytes);
312 }
313
314 fn is_commutative(&self) -> bool {
315 self.commutative
316 }
317}
318
319pub fn reduce_local_into<S, R, O>(inbuf: &S, inoutbuf: &mut R, op: O)
322where
323 S: Buffer + ?Sized,
324 R: BufferMut + ?Sized,
325 O: Operation,
326{
327 let dt = inbuf.as_datatype().id;
328 let inbytes = inbuf.as_bytes().to_vec();
329 op.reduce_bytes(dt, inoutbuf.as_bytes_mut(), &inbytes);
330}
331
332fn rt() -> &'static transport::Runtime {
337 transport::runtime()
338}
339
340fn barrier_bytes(comm: &CommData) -> io::Result<()> {
343 let ctx = comm.coll_context();
344 let n = comm.size;
345 let me = comm.rank;
346 if n <= 1 {
347 return Ok(());
348 }
349 let mut dist = 1;
350 let mut round = 0;
351 while dist < n {
352 let dst = comm.world_rank((me + dist) % n);
353 let src = (me - dist + n) % n;
354 let tag = T_BARRIER_ROUND + round;
355 rt().send(ctx, me, dst, tag, 1, DT_BYTES, &[0u8])?;
357 let _ = rt().recv(ctx, src, tag);
358 dist <<= 1;
359 round += 1;
360 }
361 Ok(())
362}
363
364pub(crate) fn bcast_bytes(comm: &CommData, root: Rank, buf: &mut [u8]) -> io::Result<()> {
366 let ctx = comm.coll_context();
367 let n = comm.size;
368 if n <= 1 {
369 return Ok(());
370 }
371 let me = comm.rank;
372 let rel = (me - root + n) % n;
373
374 let mut mask = 1;
376 while mask < n {
377 if rel & mask != 0 {
378 let src = real_rank(rel - mask, root, n);
379 let (_s, _t, _c, _d, payload) = rt().recv(ctx, src, T_BCAST);
380 let k = buf.len().min(payload.len());
381 buf[..k].copy_from_slice(&payload[..k]);
382 break;
383 }
384 mask <<= 1;
385 }
386
387 mask >>= 1;
389 while mask > 0 {
390 let child_rel = rel + mask;
391 if child_rel < n {
392 let dst = comm.world_rank(real_rank(child_rel, root, n));
393 rt().send(ctx, me, dst, T_BCAST, buf.len() as u64, DT_BYTES, buf)?;
394 }
395 mask >>= 1;
396 }
397 Ok(())
398}
399
400fn gather_bytes(
401 comm: &CommData,
402 root: Rank,
403 send: &[u8],
404 recv: Option<&mut [u8]>,
405) -> io::Result<()> {
406 let ctx = comm.coll_context();
407 let n = comm.size;
408 let blk = send.len();
409 if comm.rank == root {
410 let recv = recv.expect("root must provide a receive buffer");
411 recv[(root as usize) * blk..][..blk].copy_from_slice(send);
412 for peer in 0..n {
413 if peer != root {
414 let (_s, _t, _c, _d, payload) = rt().recv(ctx, peer, T_GATHER);
415 recv[(peer as usize) * blk..][..blk].copy_from_slice(&payload);
416 }
417 }
418 } else {
419 rt().send(
420 ctx,
421 comm.rank,
422 comm.world_rank(root),
423 T_GATHER,
424 blk as u64,
425 DT_BYTES,
426 send,
427 )?;
428 }
429 Ok(())
430}
431
432fn scatter_bytes(
433 comm: &CommData,
434 root: Rank,
435 send: Option<&[u8]>,
436 recv: &mut [u8],
437) -> io::Result<()> {
438 let ctx = comm.coll_context();
439 let n = comm.size;
440 let blk = recv.len();
441 if comm.rank == root {
442 let send = send.expect("root must provide a send buffer");
443 recv.copy_from_slice(&send[(root as usize) * blk..][..blk]);
444 for peer in 0..n {
445 if peer != root {
446 rt().send(
447 ctx,
448 comm.rank,
449 comm.world_rank(peer),
450 T_SCATTER,
451 blk as u64,
452 DT_BYTES,
453 &send[(peer as usize) * blk..][..blk],
454 )?;
455 }
456 }
457 } else {
458 let (_s, _t, _c, _d, payload) = rt().recv(ctx, root, T_SCATTER);
459 let k = recv.len().min(payload.len());
460 recv[..k].copy_from_slice(&payload[..k]);
461 }
462 Ok(())
463}
464
465fn allgather_bytes(comm: &CommData, send: &[u8], recv: &mut [u8]) -> io::Result<()> {
468 let ctx = comm.coll_context();
469 let n = comm.size;
470 let me = comm.rank;
471 let blk = send.len();
472 recv[(me as usize) * blk..][..blk].copy_from_slice(send);
474 if n <= 1 {
475 return Ok(());
476 }
477 let right = comm.world_rank((me + 1) % n);
478 let left = (me - 1 + n) % n;
479 for step in 0..n - 1 {
480 let send_idx = ((me - step + n) % n) as usize;
481 let recv_idx = ((me - step - 1 + n) % n) as usize;
482 rt().send(
483 ctx,
484 me,
485 right,
486 T_ALLGATHER_UP,
487 blk as u64,
488 DT_BYTES,
489 &recv[send_idx * blk..send_idx * blk + blk],
490 )?;
491 let (_s, _t, _c, _d, payload) = rt().recv(ctx, left, T_ALLGATHER_UP);
492 recv[recv_idx * blk..recv_idx * blk + blk].copy_from_slice(&payload);
493 }
494 Ok(())
495}
496
497fn all_to_all_bytes(comm: &CommData, send: &[u8], recv: &mut [u8]) -> io::Result<()> {
498 let ctx = comm.coll_context();
499 let n = comm.size as usize;
500 let blk = send.len().checked_div(n).unwrap_or(0);
501 let me = comm.rank as usize;
502 for peer in 0..n {
503 let block = &send[peer * blk..][..blk];
504 if peer == me {
505 recv[me * blk..][..blk].copy_from_slice(block);
506 } else {
507 rt().send(
508 ctx,
509 comm.rank,
510 comm.world_rank(peer as Rank),
511 T_ALLTOALL,
512 blk as u64,
513 DT_BYTES,
514 block,
515 )?;
516 }
517 }
518 for peer in 0..n {
519 if peer != me {
520 let (_s, _t, _c, _d, payload) = rt().recv(ctx, peer as Rank, T_ALLTOALL);
521 recv[peer * blk..][..blk].copy_from_slice(&payload);
522 }
523 }
524 Ok(())
525}
526
527fn reduce_ordered<O: Operation>(
530 comm: &CommData,
531 root: Rank,
532 send: &[u8],
533 recv: Option<&mut [u8]>,
534 op: &O,
535 dt: u32,
536) -> io::Result<()> {
537 let ctx = comm.coll_context();
538 let n = comm.size;
539 if comm.rank == root {
540 let mut contribs: Vec<Vec<u8>> = vec![Vec::new(); n as usize];
541 contribs[root as usize] = send.to_vec();
542 for peer in 0..n {
543 if peer != root {
544 let (_s, _t, _c, _d, payload) = rt().recv(ctx, peer, T_REDUCE);
545 contribs[peer as usize] = payload;
546 }
547 }
548 let mut acc = contribs[0].clone();
549 for c in contribs.iter().skip(1) {
550 op.reduce_bytes(dt, &mut acc, c);
551 }
552 recv.expect("root must provide a receive buffer")
553 .copy_from_slice(&acc);
554 } else {
555 rt().send(
556 ctx,
557 comm.rank,
558 comm.world_rank(root),
559 T_REDUCE,
560 send.len() as u64,
561 dt,
562 send,
563 )?;
564 }
565 Ok(())
566}
567
568fn reduce_bytes_op<O: Operation>(
572 comm: &CommData,
573 root: Rank,
574 send: &[u8],
575 recv: Option<&mut [u8]>,
576 op: &O,
577 dt: u32,
578) -> io::Result<()> {
579 let n = comm.size;
580 if n <= 1 {
581 if let Some(r) = recv {
582 r.copy_from_slice(send);
583 }
584 return Ok(());
585 }
586 if !op.is_commutative() {
587 return reduce_ordered(comm, root, send, recv, op, dt);
588 }
589
590 let ctx = comm.coll_context();
591 let me = comm.rank;
592 let rel = (me - root + n) % n;
593 let mut acc = send.to_vec();
594 let mut mask = 1;
595 while mask < n {
596 if rel & mask != 0 {
597 let parent = comm.world_rank(real_rank(rel - mask, root, n));
599 rt().send(ctx, me, parent, T_REDUCE, acc.len() as u64, dt, &acc)?;
600 break;
601 }
602 let child_rel = rel + mask;
604 if child_rel < n {
605 let child = real_rank(child_rel, root, n);
606 let (_s, _t, _c, _d, payload) = rt().recv(ctx, child, T_REDUCE);
607 op.reduce_bytes(dt, &mut acc, &payload);
608 }
609 mask <<= 1;
610 }
611 if rel == 0 {
612 recv.expect("root must provide a receive buffer")
613 .copy_from_slice(&acc);
614 }
615 Ok(())
616}
617
618fn all_reduce_bytes<O: Operation>(
621 comm: &CommData,
622 send: &[u8],
623 recv: &mut [u8],
624 op: &O,
625 dt: u32,
626) -> io::Result<()> {
627 let n = comm.size;
628 if n <= 1 {
629 recv.copy_from_slice(send);
630 return Ok(());
631 }
632 let is_root = comm.rank == 0;
633 {
634 let recv_opt: Option<&mut [u8]> = if is_root { Some(&mut *recv) } else { None };
635 reduce_bytes_op(comm, 0, send, recv_opt, op, dt)?;
636 }
637 bcast_bytes(comm, 0, recv)
639}
640
641fn scan_bytes<O: Operation>(
642 comm: &CommData,
643 send: &[u8],
644 recv: &mut [u8],
645 op: &O,
646 dt: u32,
647) -> io::Result<()> {
648 let ctx = comm.coll_context();
649 let n = comm.size;
650 let me = comm.rank;
651 recv.copy_from_slice(send);
652 if me > 0 {
653 let (_s, _t, _c, _d, prefix) = rt().recv(ctx, me - 1, T_SCAN);
654 let mut acc = prefix;
655 op.reduce_bytes(dt, &mut acc, send);
656 recv.copy_from_slice(&acc);
657 }
658 if me < n - 1 {
659 rt().send(
660 ctx,
661 me,
662 comm.world_rank(me + 1),
663 T_SCAN,
664 recv.len() as u64,
665 dt,
666 recv,
667 )?;
668 }
669 Ok(())
670}
671
672fn exclusive_scan_bytes<O: Operation>(
673 comm: &CommData,
674 send: &[u8],
675 recv: &mut [u8],
676 op: &O,
677 dt: u32,
678) -> io::Result<()> {
679 let ctx = comm.coll_context();
680 let n = comm.size;
681 let me = comm.rank;
682 if me > 0 {
683 let (_s, _t, _c, _d, prefix) = rt().recv(ctx, me - 1, T_EXSCAN);
684 recv.copy_from_slice(&prefix);
685 }
686 if me < n - 1 {
687 let acc = if me == 0 {
689 send.to_vec()
690 } else {
691 let mut a = recv.to_vec();
692 op.reduce_bytes(dt, &mut a, send);
693 a
694 };
695 rt().send(
696 ctx,
697 me,
698 comm.world_rank(me + 1),
699 T_EXSCAN,
700 acc.len() as u64,
701 dt,
702 &acc,
703 )?;
704 }
705 Ok(())
706}
707
708fn cerr(e: io::Error) -> crate::MpiError {
714 crate::MpiError::Other(format!("MPI collective failed: {e}"))
715}
716
717struct SendMutPtr(*mut u8);
721unsafe impl Send for SendMutPtr {}
724
725pub trait CommunicatorCollectives: Communicator {
731 fn barrier(&self) {
733 self.try_barrier().expect("MPI_Barrier failed");
734 }
735
736 fn try_barrier(&self) -> Result<(), crate::MpiError> {
738 barrier_bytes(self.comm_data()).map_err(cerr)
739 }
740
741 fn all_gather_into<S, R>(&self, sendbuf: &S, recvbuf: &mut R)
744 where
745 S: Buffer + ?Sized,
746 R: BufferMut + ?Sized,
747 {
748 self.try_all_gather_into(sendbuf, recvbuf)
749 .expect("MPI_Allgather failed");
750 }
751
752 fn try_all_gather_into<S, R>(&self, sendbuf: &S, recvbuf: &mut R) -> Result<(), crate::MpiError>
754 where
755 S: Buffer + ?Sized,
756 R: BufferMut + ?Sized,
757 {
758 let send = sendbuf.as_bytes().to_vec();
759 allgather_bytes(self.comm_data(), &send, recvbuf.as_bytes_mut()).map_err(cerr)
760 }
761
762 fn all_to_all_into<S, R>(&self, sendbuf: &S, recvbuf: &mut R)
764 where
765 S: Buffer + ?Sized,
766 R: BufferMut + ?Sized,
767 {
768 self.try_all_to_all_into(sendbuf, recvbuf)
769 .expect("MPI_Alltoall failed");
770 }
771
772 fn try_all_to_all_into<S, R>(&self, sendbuf: &S, recvbuf: &mut R) -> Result<(), crate::MpiError>
774 where
775 S: Buffer + ?Sized,
776 R: BufferMut + ?Sized,
777 {
778 let send = sendbuf.as_bytes().to_vec();
779 all_to_all_bytes(self.comm_data(), &send, recvbuf.as_bytes_mut()).map_err(cerr)
780 }
781
782 fn all_reduce_into<S, R, O>(&self, sendbuf: &S, recvbuf: &mut R, op: O)
785 where
786 S: Buffer + ?Sized,
787 R: BufferMut + ?Sized,
788 O: Operation,
789 {
790 self.try_all_reduce_into(sendbuf, recvbuf, op)
791 .expect("MPI_Allreduce failed");
792 }
793
794 fn try_all_reduce_into<S, R, O>(
796 &self,
797 sendbuf: &S,
798 recvbuf: &mut R,
799 op: O,
800 ) -> Result<(), crate::MpiError>
801 where
802 S: Buffer + ?Sized,
803 R: BufferMut + ?Sized,
804 O: Operation,
805 {
806 let dt = sendbuf.as_datatype().id;
807 let send = sendbuf.as_bytes().to_vec();
808 all_reduce_bytes(self.comm_data(), &send, recvbuf.as_bytes_mut(), &op, dt).map_err(cerr)
809 }
810
811 fn all_reduce_into_in_place<R, O>(&self, buf: &mut R, op: O)
814 where
815 R: BufferMut + ?Sized,
816 O: Operation,
817 {
818 let dt = buf.as_datatype().id;
819 let send = buf.as_bytes_mut().to_vec();
820 all_reduce_bytes(self.comm_data(), &send, buf.as_bytes_mut(), &op, dt)
821 .expect("MPI_Allreduce (in place) failed");
822 }
823
824 fn all_gather_into_in_place<R>(&self, buf: &mut R)
828 where
829 R: BufferMut + ?Sized,
830 {
831 let comm = self.comm_data();
832 let n = comm.size as usize;
833 let total = buf.as_bytes_mut().len();
834 let blk = total.checked_div(n).unwrap_or(0);
835 let me = comm.rank as usize;
836 let myblock = buf.as_bytes_mut()[me * blk..me * blk + blk].to_vec();
837 allgather_bytes(comm, &myblock, buf.as_bytes_mut())
838 .expect("MPI_Allgather (in place) failed");
839 }
840
841 fn reduce_scatter_block_into<S, R, O>(&self, sendbuf: &S, recvbuf: &mut R, op: O)
844 where
845 S: Buffer + ?Sized,
846 R: BufferMut + ?Sized,
847 O: Operation,
848 {
849 let comm = self.comm_data();
850 let dt = sendbuf.as_datatype().id;
851 let tmp = sendbuf.as_bytes().to_vec();
852 let mut reduced = tmp.clone();
853 all_reduce_bytes(comm, &tmp, &mut reduced, &op, dt)
854 .expect("MPI_Reduce_scatter_block failed");
855 let blk = recvbuf.as_bytes_mut().len();
856 let start = (comm.rank as usize) * blk;
857 recvbuf
858 .as_bytes_mut()
859 .copy_from_slice(&reduced[start..][..blk]);
860 }
861
862 fn scan_into<S, R, O>(&self, sendbuf: &S, recvbuf: &mut R, op: O)
864 where
865 S: Buffer + ?Sized,
866 R: BufferMut + ?Sized,
867 O: Operation,
868 {
869 self.try_scan_into(sendbuf, recvbuf, op)
870 .expect("MPI_Scan failed");
871 }
872
873 fn try_scan_into<S, R, O>(
875 &self,
876 sendbuf: &S,
877 recvbuf: &mut R,
878 op: O,
879 ) -> Result<(), crate::MpiError>
880 where
881 S: Buffer + ?Sized,
882 R: BufferMut + ?Sized,
883 O: Operation,
884 {
885 let dt = sendbuf.as_datatype().id;
886 let send = sendbuf.as_bytes().to_vec();
887 scan_bytes(self.comm_data(), &send, recvbuf.as_bytes_mut(), &op, dt).map_err(cerr)
888 }
889
890 fn exclusive_scan_into<S, R, O>(&self, sendbuf: &S, recvbuf: &mut R, op: O)
892 where
893 S: Buffer + ?Sized,
894 R: BufferMut + ?Sized,
895 O: Operation,
896 {
897 let dt = sendbuf.as_datatype().id;
898 let send = sendbuf.as_bytes().to_vec();
899 exclusive_scan_bytes(self.comm_data(), &send, recvbuf.as_bytes_mut(), &op, dt)
900 .expect("MPI_Exscan failed");
901 }
902
903 fn all_gather_varcount_into<S, R>(&self, sendbuf: &S, recvbuf: &mut R)
905 where
906 S: Buffer + ?Sized,
907 R: PartitionedBufferMut + ?Sized,
908 {
909 let comm = self.comm_data();
910 let send = sendbuf.as_bytes().to_vec();
911 varcount_allgather(comm, &send, recvbuf);
913 }
914
915 fn all_to_all_varcount_into<S, R>(&self, sendbuf: &S, recvbuf: &mut R)
918 where
919 S: PartitionedBuffer + ?Sized,
920 R: PartitionedBufferMut + ?Sized,
921 {
922 let comm = self.comm_data();
923 let ctx = comm.coll_context();
924 let n = comm.size;
925 let me = comm.rank;
926 let selem = sendbuf.as_datatype().size;
927 let relem = recvbuf.as_datatype().size;
928 let sc = sendbuf.counts().to_vec();
929 let sd = sendbuf.displs().to_vec();
930 let rd = recvbuf.displs().to_vec();
931 let sbytes = sendbuf.as_bytes().to_vec();
932 for peer in 0..n {
933 let off = sd[peer as usize] as usize * selem;
934 let len = sc[peer as usize] as usize * selem;
935 let block = &sbytes[off..off + len];
936 if peer == me {
937 let ro = rd[me as usize] as usize * relem;
938 recvbuf.as_bytes_mut()[ro..ro + len].copy_from_slice(block);
939 } else {
940 rt().send(
941 ctx,
942 me,
943 comm.world_rank(peer),
944 T_ALLTOALLV,
945 len as u64,
946 DT_BYTES,
947 block,
948 )
949 .expect("all_to_all_varcount");
950 }
951 }
952 for peer in 0..n {
953 if peer != me {
954 let (_s, _t, _c, _d, payload) = rt().recv(ctx, peer, T_ALLTOALLV);
955 let ro = rd[peer as usize] as usize * relem;
956 recvbuf.as_bytes_mut()[ro..ro + payload.len()].copy_from_slice(&payload);
957 }
958 }
959 }
960
961 fn immediate_barrier(&self) -> Request<'static, ()> {
965 let data = self.comm_data();
966 let comm = data.async_clone(data.derive_context(0x1BA5_1E11));
967 let handle = std::thread::spawn(move || {
968 let _ = barrier_bytes(&comm);
969 });
970 Request::from_join(StaticScope, handle)
971 }
972
973 fn immediate_all_gather_into<'a, S, R, Sc>(
975 &self,
976 scope: Sc,
977 sendbuf: &'a S,
978 recvbuf: &'a mut R,
979 ) -> Request<'a, R, Sc>
980 where
981 S: 'a + Buffer + ?Sized,
982 R: 'a + BufferMut + ?Sized,
983 Sc: Scope<'a>,
984 {
985 self.all_gather_into(sendbuf, recvbuf);
986 Request::completed(scope)
987 }
988
989 fn immediate_all_to_all_into<'a, S, R, Sc>(
991 &self,
992 scope: Sc,
993 sendbuf: &'a S,
994 recvbuf: &'a mut R,
995 ) -> Request<'a, R, Sc>
996 where
997 S: 'a + Buffer + ?Sized,
998 R: 'a + BufferMut + ?Sized,
999 Sc: Scope<'a>,
1000 {
1001 self.all_to_all_into(sendbuf, recvbuf);
1002 Request::completed(scope)
1003 }
1004
1005 fn immediate_all_reduce_into<'a, S, R, O, Sc>(
1008 &self,
1009 scope: Sc,
1010 sendbuf: &'a S,
1011 recvbuf: &'a mut R,
1012 op: O,
1013 ) -> Request<'a, R, Sc>
1014 where
1015 S: 'a + Buffer + ?Sized,
1016 R: 'a + BufferMut + ?Sized,
1017 O: Operation + Send + 'static,
1018 Sc: Scope<'a>,
1019 {
1020 let data = self.comm_data();
1021 let comm = data.async_clone(data.derive_context(0x1A11_5EED));
1022 let dt = sendbuf.as_datatype().id;
1023 let send = sendbuf.as_bytes().to_vec();
1024 let rbytes = recvbuf.as_bytes_mut();
1025 let rptr = SendMutPtr(rbytes.as_mut_ptr());
1026 let rlen = rbytes.len();
1027 let handle = std::thread::spawn(move || {
1028 let rptr = rptr; let recv = unsafe { std::slice::from_raw_parts_mut(rptr.0, rlen) };
1032 let _ = all_reduce_bytes(&comm, &send, recv, &op, dt);
1033 });
1034 Request::from_join(scope, handle)
1035 }
1036
1037 fn immediate_scan_into<'a, S, R, O, Sc>(
1039 &self,
1040 scope: Sc,
1041 sendbuf: &'a S,
1042 recvbuf: &'a mut R,
1043 op: O,
1044 ) -> Request<'a, R, Sc>
1045 where
1046 S: 'a + Buffer + ?Sized,
1047 R: 'a + BufferMut + ?Sized,
1048 O: Operation,
1049 Sc: Scope<'a>,
1050 {
1051 self.scan_into(sendbuf, recvbuf, op);
1052 Request::completed(scope)
1053 }
1054
1055 fn immediate_exclusive_scan_into<'a, S, R, O, Sc>(
1057 &self,
1058 scope: Sc,
1059 sendbuf: &'a S,
1060 recvbuf: &'a mut R,
1061 op: O,
1062 ) -> Request<'a, R, Sc>
1063 where
1064 S: 'a + Buffer + ?Sized,
1065 R: 'a + BufferMut + ?Sized,
1066 O: Operation,
1067 Sc: Scope<'a>,
1068 {
1069 self.exclusive_scan_into(sendbuf, recvbuf, op);
1070 Request::completed(scope)
1071 }
1072
1073 fn immediate_reduce_scatter_block_into<'a, S, R, O, Sc>(
1075 &self,
1076 scope: Sc,
1077 sendbuf: &'a S,
1078 recvbuf: &'a mut R,
1079 op: O,
1080 ) -> Request<'a, R, Sc>
1081 where
1082 S: 'a + Buffer + ?Sized,
1083 R: 'a + BufferMut + ?Sized,
1084 O: Operation,
1085 Sc: Scope<'a>,
1086 {
1087 self.reduce_scatter_block_into(sendbuf, recvbuf, op);
1088 Request::completed(scope)
1089 }
1090}
1091
1092impl<C: Communicator + ?Sized> CommunicatorCollectives for C {}
1094
1095fn varcount_allgather<R>(comm: &CommData, send: &[u8], recvbuf: &mut R)
1096where
1097 R: PartitionedBufferMut + ?Sized,
1098{
1099 let ctx = comm.coll_context();
1100 let n = comm.size;
1101 let root = 0;
1102 let elem = recvbuf.as_datatype().size;
1103 let counts: Vec<i32> = recvbuf.counts().to_vec();
1104 let displs: Vec<i32> = recvbuf.displs().to_vec();
1105 if comm.rank == root {
1106 let out = recvbuf.as_bytes_mut();
1107 let off = displs[root as usize] as usize * elem;
1109 out[off..off + send.len()].copy_from_slice(send);
1110 for peer in 0..n {
1111 if peer != root {
1112 let (_s, _t, _c, _d, payload) = rt().recv(ctx, peer, T_ALLGATHERV_UP);
1113 let off = displs[peer as usize] as usize * elem;
1114 out[off..off + payload.len()].copy_from_slice(&payload);
1115 }
1116 }
1117 let full = out.to_vec();
1118 for peer in 0..n {
1119 if peer != root {
1120 rt().send(
1121 ctx,
1122 comm.rank,
1123 comm.world_rank(peer),
1124 T_ALLGATHERV_DOWN,
1125 full.len() as u64,
1126 DT_BYTES,
1127 &full,
1128 )
1129 .expect("allgatherv");
1130 }
1131 }
1132 } else {
1133 let _ = &counts;
1134 rt().send(
1135 ctx,
1136 comm.rank,
1137 comm.world_rank(root),
1138 T_ALLGATHERV_UP,
1139 send.len() as u64,
1140 DT_BYTES,
1141 send,
1142 )
1143 .expect("allgatherv");
1144 let (_s, _t, _c, _d, payload) = rt().recv(ctx, root, T_ALLGATHERV_DOWN);
1145 let out = recvbuf.as_bytes_mut();
1146 let k = out.len().min(payload.len());
1147 out[..k].copy_from_slice(&payload[..k]);
1148 }
1149}
1150
1151pub trait Root {
1155 fn root_rank(&self) -> Rank;
1157
1158 #[doc(hidden)]
1159 fn root_comm(&self) -> &CommData;
1160
1161 fn broadcast_into<Buf: BufferMut + ?Sized>(&self, buffer: &mut Buf) {
1163 bcast_bytes(self.root_comm(), self.root_rank(), buffer.as_bytes_mut())
1164 .expect("MPI_Bcast failed");
1165 }
1166
1167 fn gather_into<S: Buffer + ?Sized>(&self, sendbuf: &S) {
1169 let send = sendbuf.as_bytes().to_vec();
1170 gather_bytes(self.root_comm(), self.root_rank(), &send, None).expect("MPI_Gather failed");
1171 }
1172
1173 fn gather_into_root<S, R>(&self, sendbuf: &S, recvbuf: &mut R)
1175 where
1176 S: Buffer + ?Sized,
1177 R: BufferMut + ?Sized,
1178 {
1179 let send = sendbuf.as_bytes().to_vec();
1180 gather_bytes(
1181 self.root_comm(),
1182 self.root_rank(),
1183 &send,
1184 Some(recvbuf.as_bytes_mut()),
1185 )
1186 .expect("MPI_Gather failed");
1187 }
1188
1189 fn scatter_into<R: BufferMut + ?Sized>(&self, recvbuf: &mut R) {
1191 scatter_bytes(
1192 self.root_comm(),
1193 self.root_rank(),
1194 None,
1195 recvbuf.as_bytes_mut(),
1196 )
1197 .expect("MPI_Scatter failed");
1198 }
1199
1200 fn scatter_into_root<S, R>(&self, sendbuf: &S, recvbuf: &mut R)
1203 where
1204 S: Buffer + ?Sized,
1205 R: BufferMut + ?Sized,
1206 {
1207 let send = sendbuf.as_bytes().to_vec();
1208 scatter_bytes(
1209 self.root_comm(),
1210 self.root_rank(),
1211 Some(&send),
1212 recvbuf.as_bytes_mut(),
1213 )
1214 .expect("MPI_Scatter failed");
1215 }
1216
1217 fn reduce_into<S, O>(&self, sendbuf: &S, op: O)
1220 where
1221 S: Buffer + ?Sized,
1222 O: Operation,
1223 {
1224 let dt = sendbuf.as_datatype().id;
1225 let send = sendbuf.as_bytes().to_vec();
1226 reduce_bytes_op(self.root_comm(), self.root_rank(), &send, None, &op, dt)
1227 .expect("MPI_Reduce failed");
1228 }
1229
1230 fn reduce_into_root<S, R, O>(&self, sendbuf: &S, recvbuf: &mut R, op: O)
1233 where
1234 S: Buffer + ?Sized,
1235 R: BufferMut + ?Sized,
1236 O: Operation,
1237 {
1238 let dt = sendbuf.as_datatype().id;
1239 let send = sendbuf.as_bytes().to_vec();
1240 reduce_bytes_op(
1241 self.root_comm(),
1242 self.root_rank(),
1243 &send,
1244 Some(recvbuf.as_bytes_mut()),
1245 &op,
1246 dt,
1247 )
1248 .expect("MPI_Reduce failed");
1249 }
1250
1251 fn reduce_into_root_in_place<R, O>(&self, buf: &mut R, op: O)
1255 where
1256 R: BufferMut + ?Sized,
1257 O: Operation,
1258 {
1259 let dt = buf.as_datatype().id;
1260 let send = buf.as_bytes_mut().to_vec();
1261 reduce_bytes_op(
1262 self.root_comm(),
1263 self.root_rank(),
1264 &send,
1265 Some(buf.as_bytes_mut()),
1266 &op,
1267 dt,
1268 )
1269 .expect("MPI_Reduce (in place) failed");
1270 }
1271
1272 fn gather_into_root_in_place<R>(&self, buf: &mut R)
1276 where
1277 R: BufferMut + ?Sized,
1278 {
1279 let comm = self.root_comm();
1280 let root = self.root_rank();
1281 let n = comm.size as usize;
1282 let total = buf.as_bytes_mut().len();
1283 let blk = total.checked_div(n).unwrap_or(0);
1284 let myblock = buf.as_bytes_mut()[root as usize * blk..root as usize * blk + blk].to_vec();
1285 gather_bytes(comm, root, &myblock, Some(buf.as_bytes_mut()))
1286 .expect("MPI_Gather (in place) failed");
1287 }
1288
1289 fn gather_varcount_into<S: Buffer + ?Sized>(&self, sendbuf: &S) {
1293 let comm = self.root_comm();
1294 let ctx = comm.coll_context();
1295 let send = sendbuf.as_bytes();
1296 rt().send(
1297 ctx,
1298 comm.rank,
1299 comm.world_rank(self.root_rank()),
1300 T_GATHERV,
1301 send.len() as u64,
1302 DT_BYTES,
1303 send,
1304 )
1305 .expect("gatherv");
1306 }
1307
1308 fn gather_varcount_into_root<S, R>(&self, sendbuf: &S, recvbuf: &mut R)
1310 where
1311 S: Buffer + ?Sized,
1312 R: PartitionedBufferMut + ?Sized,
1313 {
1314 let comm = self.root_comm();
1315 let root = self.root_rank();
1316 let ctx = comm.coll_context();
1317 let n = comm.size;
1318 let elem = recvbuf.as_datatype().size;
1319 let displs = recvbuf.displs().to_vec();
1320 let send = sendbuf.as_bytes().to_vec();
1321 let off = displs[root as usize] as usize * elem;
1322 recvbuf.as_bytes_mut()[off..off + send.len()].copy_from_slice(&send);
1323 for peer in 0..n {
1324 if peer != root {
1325 let (_s, _t, _c, _d, payload) = rt().recv(ctx, peer, T_GATHERV);
1326 let off = displs[peer as usize] as usize * elem;
1327 recvbuf.as_bytes_mut()[off..off + payload.len()].copy_from_slice(&payload);
1328 }
1329 }
1330 }
1331
1332 fn scatter_varcount_into<R: BufferMut + ?Sized>(&self, recvbuf: &mut R) {
1335 let comm = self.root_comm();
1336 let ctx = comm.coll_context();
1337 let (_s, _t, _c, _d, payload) = rt().recv(ctx, self.root_rank(), T_SCATTERV);
1338 let out = recvbuf.as_bytes_mut();
1339 let k = out.len().min(payload.len());
1340 out[..k].copy_from_slice(&payload[..k]);
1341 }
1342
1343 fn scatter_varcount_into_root<S, R>(&self, sendbuf: &S, recvbuf: &mut R)
1345 where
1346 S: PartitionedBuffer + ?Sized,
1347 R: BufferMut + ?Sized,
1348 {
1349 let comm = self.root_comm();
1350 let root = self.root_rank();
1351 let ctx = comm.coll_context();
1352 let n = comm.size;
1353 let elem = sendbuf.as_datatype().size;
1354 let counts = sendbuf.counts().to_vec();
1355 let displs = sendbuf.displs().to_vec();
1356 let sbytes = sendbuf.as_bytes().to_vec();
1357 for peer in 0..n {
1358 let off = displs[peer as usize] as usize * elem;
1359 let len = counts[peer as usize] as usize * elem;
1360 let block = &sbytes[off..off + len];
1361 if peer == root {
1362 let out = recvbuf.as_bytes_mut();
1363 let k = out.len().min(block.len());
1364 out[..k].copy_from_slice(&block[..k]);
1365 } else {
1366 rt().send(
1367 ctx,
1368 comm.rank,
1369 comm.world_rank(peer),
1370 T_SCATTERV,
1371 len as u64,
1372 DT_BYTES,
1373 block,
1374 )
1375 .expect("scatterv");
1376 }
1377 }
1378 }
1379
1380 fn immediate_broadcast_into<'a, Buf, Sc>(
1384 &self,
1385 scope: Sc,
1386 buffer: &'a mut Buf,
1387 ) -> Request<'a, Buf, Sc>
1388 where
1389 Buf: 'a + BufferMut + ?Sized,
1390 Sc: Scope<'a>,
1391 {
1392 self.broadcast_into(buffer);
1393 Request::completed(scope)
1394 }
1395
1396 fn immediate_gather_into<'a, S, Sc>(&self, scope: Sc, sendbuf: &'a S) -> Request<'a, S, Sc>
1398 where
1399 S: 'a + Buffer + ?Sized,
1400 Sc: Scope<'a>,
1401 {
1402 self.gather_into(sendbuf);
1403 Request::completed(scope)
1404 }
1405
1406 fn immediate_gather_into_root<'a, S, R, Sc>(
1408 &self,
1409 scope: Sc,
1410 sendbuf: &'a S,
1411 recvbuf: &'a mut R,
1412 ) -> Request<'a, R, Sc>
1413 where
1414 S: 'a + Buffer + ?Sized,
1415 R: 'a + BufferMut + ?Sized,
1416 Sc: Scope<'a>,
1417 {
1418 self.gather_into_root(sendbuf, recvbuf);
1419 Request::completed(scope)
1420 }
1421
1422 fn immediate_scatter_into<'a, R, Sc>(&self, scope: Sc, recvbuf: &'a mut R) -> Request<'a, R, Sc>
1424 where
1425 R: 'a + BufferMut + ?Sized,
1426 Sc: Scope<'a>,
1427 {
1428 self.scatter_into(recvbuf);
1429 Request::completed(scope)
1430 }
1431
1432 fn immediate_scatter_into_root<'a, S, R, Sc>(
1434 &self,
1435 scope: Sc,
1436 sendbuf: &'a S,
1437 recvbuf: &'a mut R,
1438 ) -> Request<'a, R, Sc>
1439 where
1440 S: 'a + Buffer + ?Sized,
1441 R: 'a + BufferMut + ?Sized,
1442 Sc: Scope<'a>,
1443 {
1444 self.scatter_into_root(sendbuf, recvbuf);
1445 Request::completed(scope)
1446 }
1447
1448 fn immediate_reduce_into<'a, S, O, Sc>(
1450 &self,
1451 scope: Sc,
1452 sendbuf: &'a S,
1453 op: O,
1454 ) -> Request<'a, S, Sc>
1455 where
1456 S: 'a + Buffer + ?Sized,
1457 O: Operation,
1458 Sc: Scope<'a>,
1459 {
1460 self.reduce_into(sendbuf, op);
1461 Request::completed(scope)
1462 }
1463
1464 fn immediate_reduce_into_root<'a, S, R, O, Sc>(
1466 &self,
1467 scope: Sc,
1468 sendbuf: &'a S,
1469 recvbuf: &'a mut R,
1470 op: O,
1471 ) -> Request<'a, R, Sc>
1472 where
1473 S: 'a + Buffer + ?Sized,
1474 R: 'a + BufferMut + ?Sized,
1475 O: Operation,
1476 Sc: Scope<'a>,
1477 {
1478 self.reduce_into_root(sendbuf, recvbuf, op);
1479 Request::completed(scope)
1480 }
1481
1482 fn spawn(
1487 &self,
1488 program: &str,
1489 args: &[String],
1490 maxprocs: Rank,
1491 ) -> Result<crate::topology::InterCommunicator, crate::MpiError> {
1492 crate::dpm::spawn_impl(self.root_comm(), self.root_rank(), program, args, maxprocs)
1493 }
1494}
1495
1496impl Root for Process<'_> {
1497 fn root_rank(&self) -> Rank {
1498 self.rank
1499 }
1500 fn root_comm(&self) -> &CommData {
1501 self.comm
1502 }
1503}
1504
1505#[cfg(test)]
1506mod tests {
1507 use super::*;
1508
1509 fn bytes_of<T: Copy>(v: &[T]) -> Vec<u8> {
1510 let mut out = vec![0u8; std::mem::size_of_val(v)];
1511 unsafe {
1513 std::ptr::copy_nonoverlapping(v.as_ptr() as *const u8, out.as_mut_ptr(), out.len());
1514 }
1515 out
1516 }
1517
1518 #[test]
1519 fn sum_i32() {
1520 let mut acc = bytes_of(&[1i32, 2, 3]);
1521 let val = bytes_of(&[10i32, 20, 30]);
1522 SystemOperation::sum().reduce_bytes(ids::I32, &mut acc, &val);
1523 let result: Vec<i32> = vec_from_bytes(&acc);
1524 assert_eq!(result, vec![11, 22, 33]);
1525 }
1526
1527 #[test]
1528 fn max_f64() {
1529 let mut acc = bytes_of(&[1.0f64, 5.0]);
1530 let val = bytes_of(&[3.0f64, 2.0]);
1531 SystemOperation::max().reduce_bytes(ids::F64, &mut acc, &val);
1532 let result: Vec<f64> = vec_from_bytes(&acc);
1533 assert_eq!(result, vec![3.0, 5.0]);
1534 }
1535
1536 #[test]
1537 fn bitwise_and_u8() {
1538 let mut acc = bytes_of(&[0b1100u8, 0xFF]);
1539 let val = bytes_of(&[0b1010u8, 0x0F]);
1540 SystemOperation::bitwise_and().reduce_bytes(ids::U8, &mut acc, &val);
1541 assert_eq!(acc, vec![0b1000, 0x0F]);
1542 }
1543
1544 #[test]
1545 fn product_and_min() {
1546 let mut acc = bytes_of(&[2i64, 9]);
1547 SystemOperation::product().reduce_bytes(ids::I64, &mut acc, &bytes_of(&[3i64, 3]));
1548 assert_eq!(vec_from_bytes::<i64>(&acc), vec![6, 27]);
1549
1550 let mut acc = bytes_of(&[2i64, 9]);
1551 SystemOperation::min().reduce_bytes(ids::I64, &mut acc, &bytes_of(&[3i64, 3]));
1552 assert_eq!(vec_from_bytes::<i64>(&acc), vec![2, 3]);
1553 }
1554
1555 struct Rng(u64);
1557 impl Rng {
1558 fn next(&mut self) -> u64 {
1559 let mut x = self.0;
1560 x ^= x << 13;
1561 x ^= x >> 7;
1562 x ^= x << 17;
1563 self.0 = x;
1564 x
1565 }
1566 fn i32(&mut self) -> i32 {
1567 (self.next() >> 32) as i32 / 4
1568 }
1569 }
1570
1571 type RefOp = fn(i32, i32) -> i32;
1574
1575 #[test]
1576 fn reduce_matches_reference_i32() {
1577 let mut rng = Rng(0x1234_5678_9abc_def0);
1578 let ops: &[(SystemOperation, RefOp)] = &[
1579 (SystemOperation::sum(), |a, b| a.wrapping_add(b)),
1580 (SystemOperation::product(), |a, b| a.wrapping_mul(b)),
1581 (SystemOperation::max(), |a, b| a.max(b)),
1582 (SystemOperation::min(), |a, b| a.min(b)),
1583 (SystemOperation::bitwise_and(), |a, b| a & b),
1584 (SystemOperation::bitwise_or(), |a, b| a | b),
1585 (SystemOperation::bitwise_xor(), |a, b| a ^ b),
1586 ];
1587 for _ in 0..500 {
1588 let len = (rng.next() % 8) as usize + 1;
1589 let a: Vec<i32> = (0..len).map(|_| rng.i32()).collect();
1590 let b: Vec<i32> = (0..len).map(|_| rng.i32()).collect();
1591 for (op, refn) in ops {
1592 let expected: Vec<i32> = a.iter().zip(&b).map(|(&x, &y)| refn(x, y)).collect();
1593 let mut acc = bytes_of(&a);
1594 op.reduce_bytes(ids::I32, &mut acc, &bytes_of(&b));
1595 assert_eq!(vec_from_bytes::<i32>(&acc), expected, "op mismatch");
1596 }
1597 }
1598 }
1599}