1use once_cell::sync::Lazy;
10use std::collections::{HashMap, VecDeque};
11use std::sync::atomic::{AtomicU32, Ordering};
12use std::sync::{Arc, Condvar, Mutex};
13
14use crate::mesh_error::{CommError, MeshSieveError};
15
16pub trait Wait {
18 fn wait(self) -> Option<Vec<u8>>;
20}
21
22pub trait PollWait {
24 fn try_wait(&mut self) -> Option<Vec<u8>>;
26}
27
28pub trait Communicator: Send + Sync + 'static {
32 type SendHandle: Wait;
34 type RecvHandle: Wait;
36
37 fn isend(&self, peer: usize, tag: u16, buf: &[u8]) -> Self::SendHandle;
38 fn irecv(&self, peer: usize, tag: u16, buf: &mut [u8]) -> Self::RecvHandle;
39
40 fn isend_result(
42 &self,
43 peer: usize,
44 tag: u16,
45 buf: &[u8],
46 ) -> Result<Self::SendHandle, MeshSieveError> {
47 Ok(self.isend(peer, tag, buf))
48 }
49
50 fn irecv_result(
52 &self,
53 peer: usize,
54 tag: u16,
55 buf: &mut [u8],
56 ) -> Result<Self::RecvHandle, MeshSieveError> {
57 Ok(self.irecv(peer, tag, buf))
58 }
59
60 fn is_no_comm(&self) -> bool {
62 false
63 }
64
65 fn rank(&self) -> usize;
67 fn size(&self) -> usize;
69
70 fn barrier(&self) {}
72
73 fn barrier_result(&self) -> Result<(), MeshSieveError> {
75 self.barrier();
76 Ok(())
77 }
78
79 fn broadcast(&self, root: usize, buf: &mut [u8]) {
81 if self.size() <= 1 {
82 return;
83 }
84 if self.rank() == root {
85 let mut sends = Vec::with_capacity(self.size().saturating_sub(1));
86 for peer in 0..self.size() {
87 if peer != root {
88 sends.push(self.isend(peer, COLLECTIVE_TAG_BROADCAST, buf));
89 }
90 }
91 for send in sends {
92 let _ = send.wait();
93 }
94 } else {
95 let mut tmp = vec![0u8; buf.len()];
96 let recv = self.irecv(root, COLLECTIVE_TAG_BROADCAST, &mut tmp);
97 if let Some(data) = recv.wait() {
98 let copy_len = buf.len().min(data.len());
99 buf[..copy_len].copy_from_slice(&data[..copy_len]);
100 }
101 }
102 }
103
104 fn broadcast_result(&self, root: usize, buf: &mut [u8]) -> Result<(), MeshSieveError> {
106 self.broadcast(root, buf);
107 Ok(())
108 }
109
110 fn allreduce_sum(&self, values: &mut [u64]) {
112 let size = self.size();
113 if size <= 1 {
114 return;
115 }
116 let root = 0;
117 let encoded = encode_u64_le(values);
118 if self.rank() == root {
119 let mut accum = values.to_vec();
120 let mut recvs = Vec::with_capacity(size.saturating_sub(1));
121 for peer in 0..size {
122 if peer != root {
123 let mut tmp = vec![0u8; encoded.len()];
124 let handle = self.irecv(peer, COLLECTIVE_TAG_ALLREDUCE_GATHER, &mut tmp);
125 recvs.push(handle);
126 }
127 }
128 for recv in recvs {
129 if let Some(data) = recv.wait() {
130 add_u64_le(&data, &mut accum);
131 }
132 }
133 values.copy_from_slice(&accum);
134 let out_bytes = encode_u64_le(values);
135 let mut sends = Vec::with_capacity(size.saturating_sub(1));
136 for peer in 0..size {
137 if peer != root {
138 sends.push(self.isend(peer, COLLECTIVE_TAG_ALLREDUCE_BROADCAST, &out_bytes));
139 }
140 }
141 for send in sends {
142 let _ = send.wait();
143 }
144 } else {
145 let send = self.isend(root, COLLECTIVE_TAG_ALLREDUCE_GATHER, &encoded);
146 let mut tmp = vec![0u8; encoded.len()];
147 let recv = self.irecv(root, COLLECTIVE_TAG_ALLREDUCE_BROADCAST, &mut tmp);
148 let _ = send.wait();
149 if let Some(data) = recv.wait() {
150 decode_u64_le(&data, values);
151 }
152 }
153 }
154
155 fn allreduce_sum_result(&self, values: &mut [u64]) -> Result<(), MeshSieveError> {
157 self.allreduce_sum(values);
158 Ok(())
159 }
160
161 fn allgather(&self, sendbuf: &[u8], recvbuf: &mut [u8]) {
163 let size = self.size();
164 let chunk = sendbuf.len();
165 if size == 0 || chunk == 0 {
166 return;
167 }
168 assert_eq!(
169 recvbuf.len(),
170 size * chunk,
171 "recvbuf must be size * sendbuf.len()"
172 );
173 let rank = self.rank();
174 let start = rank * chunk;
175 recvbuf[start..start + chunk].copy_from_slice(sendbuf);
176 if size <= 1 {
177 return;
178 }
179 let mut sends = Vec::with_capacity(size.saturating_sub(1));
180 let mut recvs = Vec::with_capacity(size.saturating_sub(1));
181 for peer in 0..size {
182 if peer == rank {
183 continue;
184 }
185 sends.push(self.isend(peer, COLLECTIVE_TAG_ALLGATHER, sendbuf));
186 let mut tmp = vec![0u8; chunk];
187 let recv = self.irecv(peer, COLLECTIVE_TAG_ALLGATHER, &mut tmp);
188 recvs.push((peer, recv));
189 }
190 for send in sends {
191 let _ = send.wait();
192 }
193 for (peer, recv) in recvs {
194 if let Some(data) = recv.wait() {
195 let offset = peer * chunk;
196 assert_eq!(
197 data.len(),
198 chunk,
199 "allgather received unexpected buffer length"
200 );
201 recvbuf[offset..offset + chunk].copy_from_slice(&data);
202 }
203 }
204 }
205
206 fn allgather_result(&self, sendbuf: &[u8], recvbuf: &mut [u8]) -> Result<(), MeshSieveError> {
208 self.allgather(sendbuf, recvbuf);
209 Ok(())
210 }
211
212 fn reserve_tag_range(&self, n: u16) -> Result<CommTag, MeshSieveError> {
217 reserve_tag_range(n)
218 }
219}
220
221pub const RESERVED_TAGS_START: u16 = COLLECTIVE_TAG_ALLREDUCE_BROADCAST;
223
224const COLLECTIVE_TAG_BROADCAST: u16 = u16::MAX - 1;
225const COLLECTIVE_TAG_ALLGATHER: u16 = u16::MAX - 2;
226const COLLECTIVE_TAG_ALLREDUCE_GATHER: u16 = u16::MAX - 3;
227const COLLECTIVE_TAG_ALLREDUCE_BROADCAST: u16 = u16::MAX - 4;
228
229static NEXT_TAG: AtomicU32 = AtomicU32::new(0);
230
231fn reserve_tag_range(n: u16) -> Result<CommTag, MeshSieveError> {
232 if n == 0 {
233 return Err(MeshSieveError::Communication(CommError(
234 "tag allocation requires n > 0".into(),
235 )));
236 }
237 let n = n as u32;
238 let limit = RESERVED_TAGS_START as u32;
239 let mut current = NEXT_TAG.load(Ordering::Relaxed);
240 loop {
241 let end = current + n - 1;
242 if end >= limit {
243 return Err(MeshSieveError::Communication(CommError(
244 "tag allocation exhausted available user tag range".into(),
245 )));
246 }
247 match NEXT_TAG.compare_exchange(current, end + 1, Ordering::SeqCst, Ordering::Relaxed) {
248 Ok(_) => return Ok(CommTag::new(current as u16)),
249 Err(next) => current = next,
250 }
251 }
252}
253
254fn encode_u64_le(values: &[u64]) -> Vec<u8> {
255 let mut out = Vec::with_capacity(std::mem::size_of_val(values));
256 for value in values {
257 out.extend_from_slice(&value.to_le_bytes());
258 }
259 out
260}
261
262fn decode_u64_le(bytes: &[u8], out: &mut [u64]) {
263 debug_assert_eq!(bytes.len(), std::mem::size_of_val(out));
264 for (chunk, slot) in bytes
265 .chunks_exact(core::mem::size_of::<u64>())
266 .zip(out.iter_mut())
267 {
268 let mut raw = [0u8; 8];
269 raw.copy_from_slice(chunk);
270 *slot = u64::from_le_bytes(raw);
271 }
272}
273
274fn add_u64_le(bytes: &[u8], accum: &mut [u64]) {
275 debug_assert_eq!(bytes.len(), std::mem::size_of_val(accum));
276 for (chunk, slot) in bytes
277 .chunks_exact(core::mem::size_of::<u64>())
278 .zip(accum.iter_mut())
279 {
280 let mut raw = [0u8; 8];
281 raw.copy_from_slice(chunk);
282 *slot += u64::from_le_bytes(raw);
283 }
284}
285
286#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
288pub struct CommTag(u16);
289
290impl CommTag {
291 #[inline]
293 pub const fn new(tag: u16) -> Self {
294 Self(tag)
295 }
296
297 #[inline]
299 pub const fn as_u16(self) -> u16 {
300 self.0
301 }
302
303 #[inline]
305 pub const fn offset(self, dx: u16) -> Self {
306 Self(self.0.wrapping_add(dx))
307 }
308}
309
310impl From<u16> for CommTag {
311 #[inline]
312 fn from(x: u16) -> Self {
313 CommTag::new(x)
314 }
315}
316
317#[derive(Copy, Clone, Debug)]
319pub struct SectionCommTags {
320 pub sizes: CommTag,
322 pub data: CommTag,
324}
325
326impl SectionCommTags {
327 #[inline]
329 pub const fn from_base(base: CommTag) -> Self {
330 Self {
331 sizes: base,
332 data: base.offset(1),
333 }
334 }
335}
336
337#[derive(Copy, Clone, Debug)]
339pub struct SieveCommTags {
340 pub sizes: CommTag,
342 pub data: CommTag,
344}
345
346impl SieveCommTags {
347 #[inline]
349 pub const fn from_base(base: CommTag) -> Self {
350 Self {
351 sizes: base,
352 data: base.offset(1),
353 }
354 }
355}
356
357#[derive(Copy, Clone, Debug)]
359pub struct StackCommTags {
360 pub sizes: CommTag,
362 pub data: CommTag,
364}
365
366impl StackCommTags {
367 #[inline]
369 pub const fn from_base(base: CommTag) -> Self {
370 Self {
371 sizes: base,
372 data: base.offset(1),
373 }
374 }
375}
376
377#[derive(Clone, Debug, Default)]
379pub struct NoComm;
380
381impl Wait for () {
382 fn wait(self) -> Option<Vec<u8>> {
383 None
384 }
385}
386
387impl PollWait for () {
388 fn try_wait(&mut self) -> Option<Vec<u8>> {
389 None
390 }
391}
392
393impl Communicator for NoComm {
394 type SendHandle = ();
395 type RecvHandle = ();
396
397 fn isend(&self, _peer: usize, _tag: u16, _buf: &[u8]) {}
398
399 fn irecv(&self, _peer: usize, _tag: u16, _buf: &mut [u8]) {}
400
401 fn is_no_comm(&self) -> bool {
402 true
403 }
404
405 fn rank(&self) -> usize {
406 0
407 }
408
409 fn size(&self) -> usize {
410 1
411 }
412}
413
414type Key = (usize, usize, u16); #[derive(Default)]
419struct Slot {
420 q: VecDeque<Vec<u8>>,
421}
422
423struct Mailbox {
424 map: Mutex<HashMap<Key, Arc<(Mutex<Slot>, Condvar)>>>,
425}
426
427static MAILBOX: Lazy<Mailbox> = Lazy::new(|| Mailbox {
428 map: Mutex::new(HashMap::new()),
429});
430
431fn mailbox_entry(key: Key) -> Arc<(Mutex<Slot>, Condvar)> {
432 let mut g = MAILBOX.map.lock().expect("MAILBOX poisoned");
433 g.entry(key)
434 .or_insert_with(|| Arc::new((Mutex::new(Slot::default()), Condvar::new())))
435 .clone()
436}
437
438pub struct LocalSendHandle;
439
440impl Wait for LocalSendHandle {
441 fn wait(self) -> Option<Vec<u8>> {
442 None
443 }
444}
445
446impl PollWait for LocalSendHandle {
447 fn try_wait(&mut self) -> Option<Vec<u8>> {
448 None
449 }
450}
451
452pub struct LocalRecvHandle {
453 cell: Arc<(Mutex<Slot>, Condvar)>,
454 want_len: usize,
455}
456
457impl Wait for LocalRecvHandle {
458 fn wait(self) -> Option<Vec<u8>> {
459 let (lock, cv) = &*self.cell;
460 let mut slot = lock.lock().expect("Slot poisoned");
461 while slot.q.is_empty() {
462 slot = cv.wait(slot).expect("Condvar poisoned");
463 }
464 let mut msg = slot.q.pop_front().expect("q non-empty");
465 msg.truncate(self.want_len.min(msg.len()));
466 Some(msg)
467 }
468}
469
470impl PollWait for LocalRecvHandle {
471 fn try_wait(&mut self) -> Option<Vec<u8>> {
472 let (lock, _cv) = &*self.cell;
473 let mut slot = lock.lock().expect("Slot poisoned");
474 if slot.q.is_empty() {
475 None
476 } else {
477 let mut msg = slot.q.pop_front().expect("q non-empty");
478 msg.truncate(self.want_len.min(msg.len()));
479 Some(msg)
480 }
481 }
482}
483
484#[derive(Clone, Debug)]
485pub struct RayonComm {
486 rank: usize,
487 size: usize,
488}
489
490impl RayonComm {
491 pub fn new(rank: usize, size: usize) -> Self {
492 Self { rank, size }
493 }
494}
495
496impl Communicator for RayonComm {
497 type SendHandle = LocalSendHandle;
498 type RecvHandle = LocalRecvHandle;
499
500 fn isend(&self, peer: usize, tag: u16, buf: &[u8]) -> Self::SendHandle {
501 let key = (self.rank, peer, tag);
502 let entry = mailbox_entry(key);
503 let (lock, cv) = &*entry;
504 {
505 let mut slot = lock.lock().expect("Slot poisoned");
506 slot.q.push_back(buf.to_vec());
507 }
508 cv.notify_all();
509 LocalSendHandle
510 }
511
512 fn irecv(&self, peer: usize, tag: u16, buf: &mut [u8]) -> Self::RecvHandle {
513 let key = (peer, self.rank, tag);
514 LocalRecvHandle {
515 cell: mailbox_entry(key),
516 want_len: buf.len(),
517 }
518 }
519
520 fn rank(&self) -> usize {
521 self.rank
522 }
523
524 fn size(&self) -> usize {
525 self.size
526 }
527
528 fn barrier(&self) {
529 #[cfg(test)]
530 {
531 test_barrier::set_size(self.size);
532 test_barrier::wait();
533 }
534 }
535}
536
537#[cfg(test)]
539mod test_barrier {
540 use once_cell::sync::Lazy;
541 use std::sync::{Condvar, Mutex};
542
543 pub struct EpochBarrier {
544 size: usize,
545 arrived: usize,
546 epoch: usize,
547 }
548
549 static BARRIER: Lazy<(Mutex<EpochBarrier>, Condvar)> = Lazy::new(|| {
550 (
551 Mutex::new(EpochBarrier {
552 size: 1,
553 arrived: 0,
554 epoch: 0,
555 }),
556 Condvar::new(),
557 )
558 });
559
560 pub fn set_size(size: usize) {
561 let (lock, _) = &*BARRIER;
562 let mut b = lock.lock().unwrap();
563 b.size = size;
564 }
565
566 pub fn wait() {
567 let (lock, cv) = &*BARRIER;
568 let mut b = lock.lock().unwrap();
569 let e = b.epoch;
570 b.arrived += 1;
571 if b.arrived == b.size {
572 b.arrived = 0;
573 b.epoch += 1;
574 cv.notify_all();
575 } else {
576 while e == b.epoch {
577 b = cv.wait(b).unwrap();
578 }
579 }
580 }
581}
582
583#[cfg(feature = "mpi-support")]
585mod mpi_backend {
586 use super::*;
587 use crate::mesh_error::{CommError, MeshSieveError};
588 use core::ptr::NonNull;
589 use mpi::collective::{CommunicatorCollectives, Root, SystemOperation};
590 use mpi::environment::Universe;
591 use mpi::point_to_point::{Destination, Source};
592 use mpi::topology::{Communicator as _, SimpleCommunicator};
593
594 pub struct MpiComm {
595 _universe: Universe,
596 pub world: SimpleCommunicator,
597 rank: usize,
598 size: usize,
599 }
600
601 unsafe impl Send for MpiComm {}
602 unsafe impl Sync for MpiComm {}
603
604 impl MpiComm {
605 pub fn new() -> Result<Self, MeshSieveError> {
606 let uni = mpi::initialize().ok_or_else(|| {
607 MeshSieveError::Communication(CommError("MPI initialization failed".to_string()))
608 })?;
609 let world = uni.world();
610 let rank = world.rank() as usize;
611 let size = world.size() as usize;
612 Ok(Self {
613 _universe: uni,
614 world,
615 rank,
616 size,
617 })
618 }
619 }
620
621 impl Default for MpiComm {
622 fn default() -> Self {
623 Self::new().expect("MPI initialization failed")
624 }
625 }
626
627 impl Communicator for MpiComm {
628 type SendHandle = MpiSendHandle;
629 type RecvHandle = MpiRecvHandle;
630
631 fn isend(&self, peer: usize, tag: u16, buf: &[u8]) -> Self::SendHandle {
632 use mpi::request::StaticScope;
633 let boxed = buf.to_vec().into_boxed_slice();
634 let raw: *mut [u8] = Box::into_raw(boxed);
635 let slice: &[u8] = unsafe { &*raw };
636 let req = self
637 .world
638 .process_at_rank(peer as i32)
639 .immediate_send_with_tag(StaticScope, slice, tag as i32);
640 MpiSendHandle {
641 req: Some(req),
642 buf: Some(unsafe { NonNull::new_unchecked(raw) }),
643 }
644 }
645
646 fn irecv(&self, peer: usize, tag: u16, template: &mut [u8]) -> Self::RecvHandle {
647 use mpi::request::StaticScope;
648 let len = template.len();
649 let boxed = vec![0u8; len].into_boxed_slice();
650 let raw: *mut [u8] = Box::into_raw(boxed);
651 let slice_mut: &mut [u8] = unsafe { &mut *raw };
652 let req = self
653 .world
654 .process_at_rank(peer as i32)
655 .immediate_receive_into_with_tag(StaticScope, slice_mut, tag as i32);
656 MpiRecvHandle {
657 req: Some(req),
658 buf: Some(unsafe { NonNull::new_unchecked(raw) }),
659 len,
660 }
661 }
662
663 fn rank(&self) -> usize {
664 self.rank
665 }
666 fn size(&self) -> usize {
667 self.size
668 }
669 fn barrier(&self) {
670 self.world.barrier();
671 }
672
673 fn broadcast(&self, root: usize, buf: &mut [u8]) {
674 self.world.process_at_rank(root as i32).broadcast_into(buf);
675 }
676
677 fn allreduce_sum(&self, values: &mut [u64]) {
678 let mut out = vec![0u64; values.len()];
679 self.world
680 .all_reduce_into(values, &mut out, SystemOperation::sum());
681 values.copy_from_slice(&out);
682 }
683
684 fn allgather(&self, sendbuf: &[u8], recvbuf: &mut [u8]) {
685 self.world.all_gather_into(sendbuf, recvbuf);
686 }
687 }
688
689 pub struct MpiSendHandle {
690 req: Option<mpi::request::Request<'static, [u8], mpi::request::StaticScope>>,
691 buf: Option<NonNull<[u8]>>,
692 }
693 impl Wait for MpiSendHandle {
694 fn wait(mut self) -> Option<Vec<u8>> {
695 if let Some(r) = self.req.take() {
696 let _ = r.wait();
697 }
698 if let Some(ptr) = self.buf.take() {
699 unsafe {
700 drop(Box::from_raw(ptr.as_ptr()));
701 }
702 }
703 None
704 }
705 }
706 impl Drop for MpiSendHandle {
707 fn drop(&mut self) {
708 if let Some(r) = self.req.take() {
709 let _ = r.wait();
710 }
711 if let Some(ptr) = self.buf.take() {
712 unsafe {
713 drop(Box::from_raw(ptr.as_ptr()));
714 }
715 }
716 }
717 }
718
719 pub struct MpiRecvHandle {
720 req: Option<mpi::request::Request<'static, [u8], mpi::request::StaticScope>>,
721 buf: Option<NonNull<[u8]>>,
722 len: usize,
723 }
724 impl Wait for MpiRecvHandle {
725 fn wait(mut self) -> Option<Vec<u8>> {
726 if let Some(r) = self.req.take() {
727 let _ = r.wait();
728 }
729 let ptr = self.buf.take().expect("buffer missing");
730 let boxed: Box<[u8]> = unsafe { Box::from_raw(ptr.as_ptr()) };
731 let mut v = Vec::from(boxed);
732 v.truncate(self.len);
733 Some(v)
734 }
735 }
736 impl Drop for MpiRecvHandle {
737 fn drop(&mut self) {
738 if let Some(r) = self.req.take() {
739 let _ = r.wait();
740 }
741 if let Some(ptr) = self.buf.take() {
742 unsafe {
743 drop(Box::from_raw(ptr.as_ptr()));
744 }
745 }
746 }
747 }
748}
749
750#[cfg(feature = "mpi-support")]
751pub use mpi_backend::MpiComm;