lamellar/memregion.rs
1//! Memory regions are unsafe low-level abstractions around shared memory segments that have been allocated by a lamellae provider.
2//!
3//! These memory region APIs provide the functionality to perform RDMA operations on the shared memory segments, and are at the core
4//! of how the Runtime communicates in a distributed environment (or using shared memory when using the `shmem` backend).
5//!
6//! # Warning
7//! This is a low-level module, unless you are very comfortable/confident in low level distributed memory (and even then) it is highly recommended you use the [LamellarArrays][crate::array] and [Active Messaging][crate::active_messaging] interfaces to perform distributed communications and computation.
8use crate::active_messaging::{AmDist, RemotePtr};
9use crate::array::{
10 LamellarArrayRdmaInput, LamellarArrayRdmaOutput, LamellarRead, LamellarWrite, TeamFrom,
11 TeamTryFrom,
12};
13use crate::lamellae::{AllocationType, Backend, Lamellae, LamellaeComm, LamellaeRDMA};
14use crate::lamellar_team::{LamellarTeam, LamellarTeamRT};
15use crate::LamellarEnv;
16use core::marker::PhantomData;
17use std::hash::{Hash, Hasher};
18use std::sync::Arc;
19
20//#[doc(hidden)]
21/// Prelude for using the [LamellarMemoryRegion] module
22pub mod prelude;
23
24pub(crate) mod shared;
25pub use shared::SharedMemoryRegion;
26
27pub(crate) mod one_sided;
28pub use one_sided::OneSidedMemoryRegion;
29
30pub(crate) mod handle;
31use handle::{FallibleSharedMemoryRegionHandle, SharedMemoryRegionHandle};
32
33use enum_dispatch::enum_dispatch;
34
35/// This error occurs when you are trying to directly access data locally on a PE through a memregion handle,
36/// but that PE does not contain any data for that memregion
37///
38/// This can occur when tryin to get the local data from a [OneSidedMemoryRegion] on any PE but the one which created it.
39///
40/// It can also occur if a subteam creates a shared memory region, and then a PE that does not exist in the team tries to access local data directly.
41///
42/// In both these cases the solution would be to use the memregion handle to perfrom a `get` operation, transferring the data from a remote node into a local buffer.
43#[derive(Debug, Clone)]
44pub struct MemNotLocalError;
45
46/// A Result type for LamellarMemoryRegion Operations
47pub type MemResult<T> = Result<T, MemNotLocalError>;
48
49impl std::fmt::Display for MemNotLocalError {
50 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
51 write!(f, "mem region not local",)
52 }
53}
54
55impl std::error::Error for MemNotLocalError {}
56
57/// Trait representing types that can be used in remote operations
58///
59/// as well as [Copy] so we can perform bitwise copies
60pub trait Dist:
61 AmDist + Sync + Send + Copy + serde::ser::Serialize + serde::de::DeserializeOwned + 'static
62// AmDist + Copy
63{
64}
65// impl<T: Send + Copy + std::fmt::Debug + 'static>
66// Dist for T
67// {
68// }
69
70//#[doc(hidden)]
71/// Enum used to expose common methods for all registered memory regions
72#[enum_dispatch(RegisteredMemoryRegion<T>, MemRegionId, AsBase, MemoryRegionRDMA<T>, RTMemoryRegionRDMA<T>, LamellarEnv)]
73#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
74#[serde(bound = "T: Dist + serde::Serialize + serde::de::DeserializeOwned")]
75pub enum LamellarMemoryRegion<T: Dist> {
76 ///
77 Shared(SharedMemoryRegion<T>),
78 ///
79 Local(OneSidedMemoryRegion<T>),
80 // Unsafe(UnsafeArray<T>),
81}
82
83// This could be useful for if we want to transfer the actual data instead of the pointer
84// impl<T: Dist + serde::Serialize> LamellarMemoryRegion<T> {
85// //#[tracing::instrument(skip_all)]
86// pub(crate) fn serialize_local_data<S>(
87// mr: &LamellarMemoryRegion<T>,
88// s: S,
89// ) -> Result<S::Ok, S::Error>
90// where
91// S: serde::Serializer,
92// {
93// match mr {
94// LamellarMemoryRegion::Shared(mr) => mr.serialize_local_data(s),
95// LamellarMemoryRegion::Local(mr) => mr.serialize_local_data(s),
96// // LamellarMemoryRegion::Unsafe(mr) => mr.serialize_local_data(s),
97// }
98// }
99// }
100
101impl<T: Dist> crate::active_messaging::DarcSerde for LamellarMemoryRegion<T> {
102 //#[tracing::instrument(skip_all)]
103 fn ser(&self, num_pes: usize, darcs: &mut Vec<RemotePtr>) {
104 // println!("in shared ser");
105 match self {
106 LamellarMemoryRegion::Shared(mr) => mr.ser(num_pes, darcs),
107 LamellarMemoryRegion::Local(mr) => mr.ser(num_pes, darcs),
108 // LamellarMemoryRegion::Unsafe(mr) => mr.ser(num_pes,darcs),
109 }
110 }
111 //#[tracing::instrument(skip_all)]
112 fn des(&self, cur_pe: Result<usize, crate::IdError>) {
113 // println!("in shared des");
114 match self {
115 LamellarMemoryRegion::Shared(mr) => mr.des(cur_pe),
116 LamellarMemoryRegion::Local(mr) => mr.des(cur_pe),
117 // LamellarMemoryRegion::Unsafe(mr) => mr.des(cur_pe),
118 }
119 // self.mr.print();
120 }
121}
122
123impl<T: Dist> LamellarMemoryRegion<T> {
124 //#[tracing::instrument(skip_all)]
125 /// If the memory region contains local data, return it as a mutable slice
126 /// else return an error
127 pub unsafe fn as_mut_slice(&self) -> MemResult<&mut [T]> {
128 match self {
129 LamellarMemoryRegion::Shared(memregion) => memregion.as_mut_slice(),
130 LamellarMemoryRegion::Local(memregion) => memregion.as_mut_slice(),
131 // LamellarMemoryRegion::Unsafe(memregion) => memregion.as_mut_slice(),
132 }
133 }
134
135 //#[tracing::instrument(skip_all)]
136 /// if the memory region contains local data, return it as a slice
137 /// else return an error
138 pub unsafe fn as_slice(&self) -> MemResult<&[T]> {
139 match self {
140 LamellarMemoryRegion::Shared(memregion) => memregion.as_slice(),
141 LamellarMemoryRegion::Local(memregion) => memregion.as_slice(),
142 // LamellarMemoryRegion::Unsafe(memregion) => memregion.as_slice(),
143 }
144 }
145
146 // //#[tracing::instrument(skip_all)]
147 // pub fn sub_region<R: std::ops::RangeBounds<usize>>(&self, range: R) -> LamellarMemoryRegion<T> {
148 // match self {
149 // LamellarMemoryRegion::Shared(memregion) => memregion.sub_region(range).into(),
150 // LamellarMemoryRegion::Local(memregion) => memregion.sub_region(range).into(),
151 // // LamellarMemoryRegion::Unsafe(memregion) => memregion.sub_region(range).into(),
152 // }
153 // }
154}
155impl<T: Dist> SubRegion<T> for LamellarMemoryRegion<T> {
156 type Region = LamellarMemoryRegion<T>;
157 fn sub_region<R: std::ops::RangeBounds<usize>>(&self, range: R) -> Self::Region {
158 match self {
159 LamellarMemoryRegion::Shared(memregion) => memregion.sub_region(range).into(),
160 LamellarMemoryRegion::Local(memregion) => memregion.sub_region(range).into(),
161 }
162 }
163}
164
165impl<T: Dist> From<LamellarArrayRdmaOutput<T>> for LamellarMemoryRegion<T> {
166 //#[tracing::instrument(skip_all)]
167 fn from(output: LamellarArrayRdmaOutput<T>) -> Self {
168 match output {
169 LamellarArrayRdmaOutput::LamellarMemRegion(mr) => mr,
170 LamellarArrayRdmaOutput::SharedMemRegion(mr) => mr.into(),
171 LamellarArrayRdmaOutput::LocalMemRegion(mr) => mr.into(),
172 }
173 }
174}
175
176impl<T: Dist> From<LamellarArrayRdmaInput<T>> for LamellarMemoryRegion<T> {
177 //#[tracing::instrument(skip_all)]
178 fn from(input: LamellarArrayRdmaInput<T>) -> Self {
179 match input {
180 LamellarArrayRdmaInput::LamellarMemRegion(mr) => mr,
181 LamellarArrayRdmaInput::SharedMemRegion(mr) => mr.into(),
182 LamellarArrayRdmaInput::LocalMemRegion(mr) => mr.into(),
183 }
184 }
185}
186
187impl<T: Dist> From<&LamellarMemoryRegion<T>> for LamellarArrayRdmaInput<T> {
188 //#[tracing::instrument(skip_all)]
189 fn from(mr: &LamellarMemoryRegion<T>) -> Self {
190 LamellarArrayRdmaInput::LamellarMemRegion(mr.clone())
191 }
192}
193
194impl<T: Dist> TeamFrom<&LamellarMemoryRegion<T>> for LamellarArrayRdmaInput<T> {
195 //#[tracing::instrument(skip_all)]
196 fn team_from(mr: &LamellarMemoryRegion<T>, _team: &Arc<LamellarTeam>) -> Self {
197 LamellarArrayRdmaInput::LamellarMemRegion(mr.clone())
198 }
199}
200
201impl<T: Dist> TeamFrom<LamellarMemoryRegion<T>> for LamellarArrayRdmaInput<T> {
202 //#[tracing::instrument(skip_all)]
203 fn team_from(mr: LamellarMemoryRegion<T>, _team: &Arc<LamellarTeam>) -> Self {
204 LamellarArrayRdmaInput::LamellarMemRegion(mr)
205 }
206}
207
208impl<T: Dist> TeamTryFrom<&LamellarMemoryRegion<T>> for LamellarArrayRdmaInput<T> {
209 //#[tracing::instrument(skip_all)]
210 fn team_try_from(
211 mr: &LamellarMemoryRegion<T>,
212 _team: &Arc<LamellarTeam>,
213 ) -> Result<Self, anyhow::Error> {
214 Ok(LamellarArrayRdmaInput::LamellarMemRegion(mr.clone()))
215 }
216}
217
218impl<T: Dist> TeamTryFrom<LamellarMemoryRegion<T>> for LamellarArrayRdmaInput<T> {
219 //#[tracing::instrument(skip_all)]
220 fn team_try_from(
221 mr: LamellarMemoryRegion<T>,
222 _team: &Arc<LamellarTeam>,
223 ) -> Result<Self, anyhow::Error> {
224 Ok(LamellarArrayRdmaInput::LamellarMemRegion(mr))
225 }
226}
227
228impl<T: Dist> From<&LamellarMemoryRegion<T>> for LamellarArrayRdmaOutput<T> {
229 //#[tracing::instrument(skip_all)]
230 fn from(mr: &LamellarMemoryRegion<T>) -> Self {
231 LamellarArrayRdmaOutput::LamellarMemRegion(mr.clone())
232 }
233}
234
235impl<T: Dist> TeamFrom<&LamellarMemoryRegion<T>> for LamellarArrayRdmaOutput<T> {
236 //#[tracing::instrument(skip_all)]
237 fn team_from(mr: &LamellarMemoryRegion<T>, _team: &Arc<LamellarTeam>) -> Self {
238 LamellarArrayRdmaOutput::LamellarMemRegion(mr.clone())
239 }
240}
241
242impl<T: Dist> TeamFrom<LamellarMemoryRegion<T>> for LamellarArrayRdmaOutput<T> {
243 //#[tracing::instrument(skip_all)]
244 fn team_from(mr: LamellarMemoryRegion<T>, _team: &Arc<LamellarTeam>) -> Self {
245 LamellarArrayRdmaOutput::LamellarMemRegion(mr)
246 }
247}
248
249impl<T: Dist> TeamTryFrom<&LamellarMemoryRegion<T>> for LamellarArrayRdmaOutput<T> {
250 //#[tracing::instrument(skip_all)]
251 fn team_try_from(
252 mr: &LamellarMemoryRegion<T>,
253 _team: &Arc<LamellarTeam>,
254 ) -> Result<Self, anyhow::Error> {
255 Ok(LamellarArrayRdmaOutput::LamellarMemRegion(mr.clone()))
256 }
257}
258
259impl<T: Dist> TeamTryFrom<LamellarMemoryRegion<T>> for LamellarArrayRdmaOutput<T> {
260 //#[tracing::instrument(skip_all)]
261 fn team_try_from(
262 mr: LamellarMemoryRegion<T>,
263 _team: &Arc<LamellarTeam>,
264 ) -> Result<Self, anyhow::Error> {
265 Ok(LamellarArrayRdmaOutput::LamellarMemRegion(mr))
266 }
267}
268/// An abstraction for a memory region that has been registered with the underlying lamellae (network provider)
269/// allowing for RDMA operations.
270///
271/// Memory Regions are low-level unsafe abstraction not really intended for use in higher-level applications
272///
273///
274/// Unless you are very confident in low level distributed memory access it is highly recommended you utilize the
275/// [LamellarArray][crate::array::LamellarArray] interface to construct and interact with distributed memory.
276#[enum_dispatch]
277pub trait RegisteredMemoryRegion<T: Dist> {
278 #[doc(alias("One-sided", "onesided"))]
279 /// The length (in number of elements of `T`) of the local segment of the memory region (i.e. not the global length of the memory region)
280 ///
281 /// # One-sided Operation
282 /// the result is returned only on the calling PE
283 ///
284 /// # Examples
285 ///```
286 /// use lamellar::memregion::prelude::*;
287 ///
288 /// let world = LamellarWorldBuilder::new().build();
289 ///
290 /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
291 /// assert_eq!(mem_region.len(),1000);
292 ///```
293 fn len(&self) -> usize;
294
295 //TODO: move this function to a private trait or private method
296 #[doc(hidden)]
297 fn addr(&self) -> MemResult<usize>;
298
299 #[doc(alias("One-sided", "onesided"))]
300 /// Return a slice of the local (to the calling PE) data of the memory region
301 ///
302 /// Returns an error if the PE does not contain any local data associated with this memory region
303 ///
304 /// # Safety
305 /// this call is always unsafe as there is no gaurantee that there do not exist mutable references elsewhere in the distributed system.
306 ///
307 /// # One-sided Operation
308 /// the result is returned only on the calling PE
309 ///
310 /// # Examples
311 ///```
312 /// use lamellar::memregion::prelude::*;
313 ///
314 /// let world = LamellarWorldBuilder::new().build();
315 ///
316 /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
317 /// let slice = unsafe{mem_region.as_slice().expect("PE is part of the world team")};
318 ///```
319 unsafe fn as_slice(&self) -> MemResult<&[T]>;
320
321 #[doc(alias("One-sided", "onesided"))]
322 /// Return a reference to the local (to the calling PE) element located by the provided index
323 ///
324 /// Returns an error if the index is out of bounds or the PE does not contain any local data associated with this memory region
325 ///
326 /// # Safety
327 /// this call is always unsafe as there is no gaurantee that there do not exist mutable references elsewhere in the distributed system.
328 ///
329 /// # One-sided Operation
330 /// the result is returned only on the calling PE
331 ///
332 /// # Examples
333 ///```
334 /// use lamellar::memregion::prelude::*;
335 ///
336 /// let world = LamellarWorldBuilder::new().build();
337 ///
338 /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
339 /// let val = unsafe{mem_region.at(999).expect("PE is part of the world team")};
340 ///```
341 unsafe fn at(&self, index: usize) -> MemResult<&T>;
342
343 #[doc(alias("One-sided", "onesided"))]
344 /// Return a mutable slice of the local (to the calling PE) data of the memory region
345 ///
346 /// Returns an error if the PE does not contain any local data associated with this memory region
347 ///
348 /// # Safety
349 /// this call is always unsafe as there is no gaurantee that there do not exist other mutable references elsewhere in the distributed system.
350 ///
351 /// # One-sided Operation
352 /// the result is returned only on the calling PE
353 ///
354 /// # Examples
355 ///```
356 /// use lamellar::memregion::prelude::*;
357 ///
358 /// let world = LamellarWorldBuilder::new().build();
359 ///
360 /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
361 /// let slice =unsafe { mem_region.as_mut_slice().expect("PE is part of the world team")};
362 ///```
363 unsafe fn as_mut_slice(&self) -> MemResult<&mut [T]>;
364
365 #[doc(alias("One-sided", "onesided"))]
366 /// Return a ptr to the local (to the calling PE) data of the memory region
367 ///
368 /// Returns an error if the PE does not contain any local data associated with this memory region
369 ///
370 /// # Safety
371 /// this call is always unsafe as there is no gaurantee that there do not exist mutable references elsewhere in the distributed system.
372 ///
373 /// # One-sided Operation
374 /// the result is returned only on the calling PE
375 ///
376 /// # Examples
377 ///```
378 /// use lamellar::memregion::prelude::*;
379 ///
380 /// let world = LamellarWorldBuilder::new().build();
381 ///
382 /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
383 /// let ptr = unsafe { mem_region.as_ptr().expect("PE is part of the world team")};
384 ///```
385 unsafe fn as_ptr(&self) -> MemResult<*const T>;
386
387 #[doc(alias("One-sided", "onesided"))]
388 /// Return a mutable ptr to the local (to the calling PE) data of the memory region
389 ///
390 /// Returns an error if the PE does not contain any local data associated with this memory region
391 ///
392 /// # Safety
393 /// this call is always unsafe as there is no gaurantee that there do not exist mutable references elsewhere in the distributed system.
394 ///
395 /// # One-sided Operation
396 /// the result is returned only on the calling PE
397 ///
398 /// # Examples
399 ///```
400 /// use lamellar::memregion::prelude::*;
401 ///
402 /// let world = LamellarWorldBuilder::new().build();
403 ///
404 /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(1000).block();
405 /// let ptr = unsafe { mem_region.as_mut_ptr().expect("PE is part of the world team")};
406 ///```
407 unsafe fn as_mut_ptr(&self) -> MemResult<*mut T>;
408}
409
410#[enum_dispatch]
411pub(crate) trait MemRegionId {
412 fn id(&self) -> usize;
413}
414
415// RegisteredMemoryRegion<T>, MemRegionId, AsBase, SubRegion<T>, MemoryRegionRDMA<T>, RTMemoryRegionRDMA<T>
416// we seperate SubRegion and AsBase out as their own traits
417// because we want MemRegion to impl RegisteredMemoryRegion (so that it can be used in Shared + Local)
418// but MemRegion should not return LamellarMemoryRegions directly (as both SubRegion and AsBase require)
419// we will implement seperate functions for MemoryRegion itself.
420//#[doc(hidden)]
421
422/// Trait for creating subregions of a memory region
423#[enum_dispatch]
424pub trait SubRegion<T: Dist> {
425 #[doc(hidden)]
426 type Region: RegisteredMemoryRegion<T> + MemoryRegionRDMA<T>;
427 #[doc(alias("One-sided", "onesided"))]
428 /// Create a sub region of this RegisteredMemoryRegion using the provided range
429 ///
430 /// # One-sided Operation
431 /// the result is returned only on the calling PE
432 ///
433 /// # Panics
434 /// panics if the end range is larger than the length of the memory region
435 /// # Examples
436 ///```
437 /// use lamellar::memregion::prelude::*;
438 ///
439 /// let world = LamellarWorldBuilder::new().build();
440 /// let my_pe = world.my_pe();
441 /// let num_pes = world.num_pes();
442 ///
443 /// let mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(100).block();
444 ///
445 /// let sub_region = mem_region.sub_region(30..70);
446 ///```
447 fn sub_region<R: std::ops::RangeBounds<usize>>(&self, range: R) -> Self::Region;
448}
449
450#[enum_dispatch]
451pub(crate) trait AsBase {
452 unsafe fn to_base<B: Dist>(self) -> LamellarMemoryRegion<B>;
453}
454
455/// The Inteface for exposing RDMA operations on a memory region. These provide the actual mechanism for performing a transfer.
456#[enum_dispatch]
457pub trait MemoryRegionRDMA<T: Dist> {
458 #[doc(alias("One-sided", "onesided"))]
459 /// "Puts" (copies) data from a local memory location into a remote memory location on the specified PE
460 ///
461 /// The data buffer may not be safe to upon return from this call, currently the user is responsible for completion detection,
462 /// or you may use the similar blocking_put call (with a potential performance penalty);
463 ///
464 /// # Safety
465 /// This call is always unsafe as mutual exclusitivity is not enforced, i.e. many other reader/writers can exist simultaneously.
466 /// Additionally, when this call returns the underlying fabric provider may or may not have already copied the data buffer
467 ///
468 /// # One-sided Operation
469 /// the calling PE initaites the remote transfer
470 ///
471 /// # Examples
472 ///```
473 /// use lamellar::memregion::prelude::*;
474 ///
475 /// let world = LamellarWorldBuilder::new().build();
476 /// let my_pe = world.my_pe();
477 /// let num_pes = world.num_pes();
478 ///
479 /// let dst_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(num_pes*10).block();
480 /// let src_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(10).block();
481 /// unsafe{ for elem in dst_mem_region.as_mut_slice().expect("PE in world team") {*elem = num_pes;}}
482 /// unsafe{ for elem in src_mem_region.as_mut_slice().expect("PE in world team") {*elem = my_pe;}}
483 ///
484 /// for pe in 0..num_pes{
485 /// unsafe{dst_mem_region.put(pe,my_pe*src_mem_region.len(),&src_mem_region)};
486 /// }
487 /// unsafe {
488 /// let dst_slice = dst_mem_region.as_slice().expect("PE in world team");
489 /// for (i,elem) in dst_slice.iter().enumerate(){
490 /// let pe = i / &src_mem_region.len();
491 /// while *elem == num_pes{
492 /// std::thread::yield_now();
493 /// }
494 /// assert_eq!(pe,*elem);
495 /// }
496 /// }
497 ///```
498 unsafe fn put<U: Into<LamellarMemoryRegion<T>>>(&self, pe: usize, index: usize, data: U);
499
500 #[doc(alias("One-sided", "onesided"))]
501 /// Blocking "Puts" (copies) data from a local memory location into a remote memory location on the specified PE.
502 ///
503 /// This function blocks until the data in the data buffer has been transfered out of this PE, this does not imply that it has arrived at the remote destination though
504 ///
505 /// # Safety
506 /// This call is always unsafe as mutual exclusitivity is not enforced, i.e. many other reader/writers can exist simultaneously.
507 ///
508 /// # One-sided Operation
509 /// the calling PE initaites the remote transfer
510 ///
511 /// # Examples
512 ///```
513 /// use lamellar::memregion::prelude::*;
514 ///
515 /// let world = LamellarWorldBuilder::new().build();
516 /// let my_pe = world.my_pe();
517 /// let num_pes = world.num_pes();
518 ///
519 /// let dst_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(num_pes*10).block();
520 /// let src_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(10).block();
521 /// unsafe{ for elem in dst_mem_region.as_mut_slice().expect("PE in world team") {*elem = num_pes;}}
522 /// unsafe{ for elem in src_mem_region.as_mut_slice().expect("PE in world team") {*elem = my_pe;}}
523 ///
524 /// for pe in 0..num_pes{
525 /// unsafe{dst_mem_region.blocking_put(pe,my_pe*src_mem_region.len(),&src_mem_region)};
526 /// }
527 /// unsafe {
528 /// let dst_slice = dst_mem_region.as_slice().expect("PE in world team");
529 /// for (i,elem) in dst_slice.iter().enumerate(){
530 /// let pe = i / &src_mem_region.len();
531 /// while *elem == num_pes{
532 /// std::thread::yield_now();
533 /// }
534 /// assert_eq!(pe,*elem);
535 /// }
536 /// }
537 ///```
538 unsafe fn blocking_put<U: Into<LamellarMemoryRegion<T>>>(
539 &self,
540 pe: usize,
541 index: usize,
542 data: U,
543 );
544
545 /// "Puts" (copies) data from a local memory location into a remote memory location on all PEs containing the memory region
546 ///
547 /// This is similar to broadcast
548 ///
549 /// The data buffer may not be safe to upon return from this call, currently the user is responsible for completion detection
550 ///
551 /// # Safety
552 /// This call is always unsafe as mutual exclusitivity is not enforced, i.e. many other reader/writers can exist simultaneously.
553 /// Additionally, when this call returns the underlying fabric provider may or may not have already copied the data buffer
554 /// # Examples
555 ///```
556 /// use lamellar::memregion::prelude::*;
557 ///
558 /// let world = LamellarWorldBuilder::new().build();
559 /// let my_pe = world.my_pe();
560 /// let num_pes = world.num_pes();
561 ///
562 /// let dst_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(num_pes*10).block();
563 /// let src_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(10).block();
564 /// unsafe{ for elem in dst_mem_region.as_mut_slice().expect("PE in world team") {*elem = num_pes;}}
565 /// unsafe{ for elem in src_mem_region.as_mut_slice().expect("PE in world team") {*elem = my_pe;}}
566 ///
567 /// unsafe{dst_mem_region.put_all(my_pe*src_mem_region.len(),&src_mem_region)};
568 ///
569 /// unsafe {
570 /// let dst_slice = dst_mem_region.as_slice().expect("PE in world team");
571 /// for (i,elem) in dst_slice.iter().enumerate(){
572 /// let pe = i / &src_mem_region.len();
573 /// while *elem == num_pes{
574 /// std::thread::yield_now();
575 /// }
576 /// assert_eq!(pe,*elem);
577 /// }
578 /// }
579 ///```
580 unsafe fn put_all<U: Into<LamellarMemoryRegion<T>>>(&self, index: usize, data: U);
581
582 #[doc(alias("One-sided", "onesided"))]
583 /// "Gets" (copies) data from remote memory location on the specified PE into the provided data buffer.
584 /// After calling this function, the data may or may not have actually arrived into the data buffer.
585 /// The user is responsible for transmission termination detection
586 ///
587 /// # Safety
588 /// This call is always unsafe as mutual exclusitivity is not enforced, i.e. many other reader/writers can exist simultaneously.
589 /// Additionally, when this call returns the underlying fabric provider may or may not have already copied data into the data buffer.
590 ///
591 /// # One-sided Operation
592 /// the calling PE initaites the remote transfer
593 ///
594 /// # Examples
595 ///```
596 /// use lamellar::memregion::prelude::*;
597 ///
598 /// let world = LamellarWorldBuilder::new().build();
599 /// let my_pe = world.my_pe();
600 /// let num_pes = world.num_pes();
601 ///
602 /// let src_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(10).block();
603 /// let dst_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(num_pes*10).block();
604 ///
605 /// unsafe{ for elem in src_mem_region.as_mut_slice().expect("PE in world team") {*elem = my_pe;}}
606 /// unsafe{ for elem in dst_mem_region.as_mut_slice().expect("PE in world team") {*elem = num_pes;}}
607 ///
608 /// for pe in 0..num_pes{
609 /// let start_i = pe*src_mem_region.len();
610 /// let end_i = start_i+src_mem_region.len();
611 /// unsafe{src_mem_region.get_unchecked(pe,0,dst_mem_region.sub_region(start_i..end_i))};
612 /// }
613 ///
614 /// unsafe {
615 /// let dst_slice = dst_mem_region.as_slice().expect("PE in world team");
616 /// for (i,elem) in dst_slice.iter().enumerate(){
617 /// let pe = i / &src_mem_region.len();
618 /// while *elem == num_pes{
619 /// std::thread::yield_now();
620 /// }
621 /// assert_eq!(pe,*elem);
622 /// }
623 /// }
624 ///```
625 unsafe fn get_unchecked<U: Into<LamellarMemoryRegion<T>>>(
626 &self,
627 pe: usize,
628 index: usize,
629 data: U,
630 );
631
632 #[doc(alias("One-sided", "onesided"))]
633 /// Blocking "Gets" (copies) data from remote memory location on the specified PE into the provided data buffer.
634 /// After calling this function, the data is guaranteed to be placed in the data buffer
635 ///
636 /// # Safety
637 /// This call is always unsafe as mutual exclusitivity is not enforced, i.e. many other reader/writers can exist simultaneously.
638 ///
639 /// # One-sided Operation
640 /// the calling PE initaites the remote transfer
641 ///
642 /// # Examples
643 ///```
644 /// use lamellar::memregion::prelude::*;
645 ///
646 /// let world = LamellarWorldBuilder::new().build();
647 /// let my_pe = world.my_pe();
648 /// let num_pes = world.num_pes();
649 ///
650 /// let src_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(10).block();
651 /// let dst_mem_region: SharedMemoryRegion<usize> = world.alloc_shared_mem_region(num_pes*10).block();
652 ///
653 /// unsafe{ for elem in src_mem_region.as_mut_slice().expect("PE in world team") {*elem = my_pe;}}
654 /// unsafe{ for elem in dst_mem_region.as_mut_slice().expect("PE in world team") {*elem = num_pes;}}
655 ///
656 /// for pe in 0..num_pes{
657 /// let start_i = pe*src_mem_region.len();
658 /// let end_i = start_i+src_mem_region.len();
659 /// unsafe{src_mem_region.blocking_get(pe,0,dst_mem_region.sub_region(start_i..end_i))};
660 /// }
661 ///
662 /// unsafe {
663 /// let dst_slice = dst_mem_region.as_slice().expect("PE in world team");
664 /// for (i,elem) in dst_slice.iter().enumerate(){
665 /// let pe = i / &src_mem_region.len();
666 /// assert_eq!(pe,*elem);
667 /// }
668 /// }
669 ///```
670 unsafe fn blocking_get<U: Into<LamellarMemoryRegion<T>>>(
671 &self,
672 pe: usize,
673 index: usize,
674 data: U,
675 );
676}
677
678#[enum_dispatch]
679pub(crate) trait RTMemoryRegionRDMA<T: Dist> {
680 unsafe fn put_slice(&self, pe: usize, index: usize, data: &[T]);
681 unsafe fn blocking_get_slice(&self, pe: usize, index: usize, data: &mut [T]);
682}
683
684impl<T: Dist> Hash for LamellarMemoryRegion<T> {
685 //#[tracing::instrument(skip_all)]
686 fn hash<H: Hasher>(&self, state: &mut H) {
687 self.id().hash(state);
688 }
689}
690
691impl<T: Dist> PartialEq for LamellarMemoryRegion<T> {
692 //#[tracing::instrument(skip_all)]
693 fn eq(&self, other: &LamellarMemoryRegion<T>) -> bool {
694 self.id() == other.id()
695 }
696}
697
698impl<T: Dist> Eq for LamellarMemoryRegion<T> {}
699
700impl<T: Dist> LamellarWrite for LamellarMemoryRegion<T> {}
701impl<T: Dist> LamellarWrite for &LamellarMemoryRegion<T> {}
702impl<T: Dist> LamellarRead for LamellarMemoryRegion<T> {}
703impl<T: Dist> LamellarRead for &LamellarMemoryRegion<T> {}
704
705#[derive(Copy, Clone)]
706pub(crate) enum Mode {
707 Local,
708 Remote,
709 Shared,
710}
711
712// this is not intended to be accessed directly by a user
713// it will be wrapped in either a shared region or local region
714// in shared regions its wrapped in a darc which allows us to send
715// to different nodes, in local its wrapped in Arc (we dont currently support sending to other nodes)
716// for local we would probably need to develop something like a one-sided initiated darc...
717pub(crate) struct MemoryRegion<T: Dist> {
718 addr: usize,
719 pe: usize,
720 size: usize,
721 num_bytes: usize,
722 backend: Backend,
723 rdma: Arc<dyn LamellaeRDMA>,
724 mode: Mode,
725 phantom: PhantomData<T>,
726}
727
728impl<T: Dist> MemoryRegion<T> {
729 //#[tracing::instrument(skip_all)]
730 pub(crate) fn new(
731 size: usize, //number of elements of type T
732 lamellae: Arc<Lamellae>,
733 alloc: AllocationType,
734 ) -> MemoryRegion<T> {
735 if let Ok(memreg) = MemoryRegion::try_new(size, lamellae, alloc) {
736 memreg
737 } else {
738 unsafe { std::ptr::null_mut::<i32>().write(1) };
739 panic!("out of memory")
740 }
741 }
742 //#[tracing::instrument(skip_all)]
743 pub(crate) fn try_new(
744 size: usize, //number of elements of type T
745 lamellae: Arc<Lamellae>,
746 alloc: AllocationType,
747 ) -> Result<MemoryRegion<T>, anyhow::Error> {
748 // println!(
749 // "creating new lamellar memory region size: {:?} align: {:?}",
750 // size * std::mem::size_of::<T>(),
751 // std::mem::align_of::<T>()
752 // );
753 let mut mode = Mode::Shared;
754 let addr = if size > 0 {
755 if let AllocationType::Local = alloc {
756 mode = Mode::Local;
757 lamellae.rt_alloc(size * std::mem::size_of::<T>(), std::mem::align_of::<T>())?
758 } else {
759 lamellae.alloc(
760 size * std::mem::size_of::<T>(),
761 alloc,
762 std::mem::align_of::<T>(),
763 )? //did we call team barrer before this?
764 }
765 } else {
766 println!(
767 "cant have zero sized memregion {:?}",
768 std::backtrace::Backtrace::capture()
769 );
770 panic!("cant have zero sized memregion");
771 // return Err(anyhow::anyhow!("cant have negative sized memregion"));
772 };
773 let temp = MemoryRegion {
774 addr: addr,
775 pe: lamellae.my_pe(),
776 size: size,
777 num_bytes: size * std::mem::size_of::<T>(),
778 backend: lamellae.backend(),
779 rdma: lamellae,
780 mode: mode,
781 phantom: PhantomData,
782 };
783 // println!(
784 // "new memregion {:x} {:x}",
785 // temp.addr,
786 // size * std::mem::size_of::<T>()
787 // );
788 Ok(temp)
789 }
790 //#[tracing::instrument(skip_all)]
791 pub(crate) fn from_remote_addr(
792 addr: usize,
793 pe: usize,
794 size: usize,
795 lamellae: Arc<Lamellae>,
796 ) -> Result<MemoryRegion<T>, anyhow::Error> {
797 Ok(MemoryRegion {
798 addr: addr,
799 pe: pe,
800 size: size,
801 num_bytes: size * std::mem::size_of::<T>(),
802 backend: lamellae.backend(),
803 rdma: lamellae,
804 mode: Mode::Remote,
805 phantom: PhantomData,
806 })
807 }
808
809 #[allow(dead_code)]
810 //#[tracing::instrument(skip_all)]
811 pub(crate) unsafe fn to_base<B: Dist>(mut self) -> MemoryRegion<B> {
812 //this is allowed as we consume the old object..
813 assert_eq!(
814 self.num_bytes % std::mem::size_of::<B>(),
815 0,
816 "Error converting memregion to new base, does not align"
817 );
818 // MemoryRegion {
819 // addr: self.addr, //TODO: out of memory...
820 // pe: self.pe,
821 // size: self.num_bytes / std::mem::size_of::<B>(),
822 // num_bytes: self.num_bytes,
823 // backend: self.backend,
824 // rdma: self.rdma.clone(),
825 // mode: self.mode,
826 // phantom: PhantomData,
827 // }
828 self.size = self.num_bytes / std::mem::size_of::<B>();
829 std::mem::transmute(self) //we do this because other wise self gets dropped and frees the underlying data (we could also set addr to 0 in self)
830 }
831
832 // }
833
834 // impl<T: AmDist+ 'static> MemoryRegionRDMA<T> for MemoryRegion<T> {
835 /// copy data from local memory location into a remote memory location
836 ///
837 /// # Arguments
838 ///
839 /// * `pe` - id of remote PE to grab data from
840 /// * `index` - offset into the remote memory window
841 /// * `data` - address (which is "registered" with network device) of local input buffer that will be put into the remote memory
842 /// the data buffer may not be safe to upon return from this call, currently the user is responsible for completion detection,
843 /// or you may use the similar iput call (with a potential performance penalty);
844 //#[tracing::instrument(skip_all)]
845 pub(crate) unsafe fn put<R: Dist, U: Into<LamellarMemoryRegion<R>>>(
846 &self,
847 pe: usize,
848 index: usize,
849 data: U,
850 ) {
851 //todo make return a result?
852 let data = data.into();
853 if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
854 let num_bytes = data.len() * std::mem::size_of::<R>();
855 if let Ok(ptr) = data.as_ptr() {
856 let bytes = std::slice::from_raw_parts(ptr as *const u8, num_bytes);
857 self.rdma
858 .put(pe, bytes, self.addr + index * std::mem::size_of::<R>())
859 } else {
860 panic!("ERROR: put data src is not local");
861 }
862 } else {
863 println!(
864 "mem region bytes: {:?} sizeof elem {:?} len {:?}",
865 self.num_bytes,
866 std::mem::size_of::<T>(),
867 self.size
868 );
869 println!(
870 "data bytes: {:?} sizeof elem {:?} len {:?} index: {:?}",
871 data.len() * std::mem::size_of::<R>(),
872 std::mem::size_of::<R>(),
873 data.len(),
874 index
875 );
876 panic!("index out of bounds");
877 }
878 }
879
880 /// copy data from local memory location into a remote memory localtion
881 ///
882 /// # Arguments
883 ///
884 /// * `pe` - id of remote PE to grab data from
885 /// * `index` - offset into the remote memory window
886 /// * `data` - address (which is "registered" with network device) of local input buffer that will be put into the remote memory
887 /// the data buffer is free to be reused upon return of this function.
888 //#[tracing::instrument(skip_all)]
889 pub(crate) unsafe fn blocking_put<R: Dist, U: Into<LamellarMemoryRegion<R>>>(
890 &self,
891 pe: usize,
892 index: usize,
893 data: U,
894 ) {
895 //todo make return a result?
896 let data = data.into();
897 if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
898 let num_bytes = data.len() * std::mem::size_of::<R>();
899 if let Ok(ptr) = data.as_ptr() {
900 let bytes = std::slice::from_raw_parts(ptr as *const u8, num_bytes);
901 self.rdma
902 .iput(pe, bytes, self.addr + index * std::mem::size_of::<R>())
903 } else {
904 panic!("ERROR: put data src is not local");
905 }
906 } else {
907 println!("{:?} {:?} {:?}", self.size, index, data.len());
908 panic!("index out of bounds");
909 }
910 }
911
912 //#[tracing::instrument(skip_all)]
913 pub(crate) unsafe fn put_all<R: Dist, U: Into<LamellarMemoryRegion<R>>>(
914 &self,
915 index: usize,
916 data: U,
917 ) {
918 let data = data.into();
919 if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
920 let num_bytes = data.len() * std::mem::size_of::<R>();
921 if let Ok(ptr) = data.as_ptr() {
922 let bytes = std::slice::from_raw_parts(ptr as *const u8, num_bytes);
923 self.rdma
924 .put_all(bytes, self.addr + index * std::mem::size_of::<R>());
925 } else {
926 panic!("ERROR: put data src is not local");
927 }
928 } else {
929 panic!("index out of bounds");
930 }
931 }
932
933 //TODO: once we have a reliable asynchronos get wait mechanism, we return a request handle,
934 //data probably needs to be referenced count or lifespan controlled so we know it exists when the get trys to complete
935 //in the handle drop method we will wait until the request completes before dropping... ensuring the data has a place to go
936 /// copy data from remote memory location into provided data buffer
937 ///
938 /// # Arguments
939 ///
940 /// * `pe` - id of remote PE to grab data from
941 /// * `index` - offset into the remote memory window
942 /// * `data` - address (which is "registered" with network device) of destination buffer to store result of the get
943 //#[tracing::instrument(skip_all)]
944 pub(crate) unsafe fn get_unchecked<R: Dist, U: Into<LamellarMemoryRegion<R>>>(
945 &self,
946 pe: usize,
947 index: usize,
948 data: U,
949 ) {
950 let data = data.into();
951 if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
952 let num_bytes = data.len() * std::mem::size_of::<R>();
953 if let Ok(ptr) = data.as_mut_ptr() {
954 let bytes = std::slice::from_raw_parts_mut(ptr as *mut u8, num_bytes);
955 // println!("getting {:?} {:?} {:?} {:?} {:?} {:?} {:?}",pe,index,std::mem::size_of::<R>(),data.len(), num_bytes,self.size, self.num_bytes);
956 self.rdma
957 .get(pe, self.addr + index * std::mem::size_of::<R>(), bytes);
958 //(remote pe, src, dst)
959 // println!("getting {:?} {:?} [{:?}] {:?} {:?} {:?}",pe,self.addr + index * std::mem::size_of::<T>(),index,data.addr(),data.len(),num_bytes);
960 } else {
961 panic!("ERROR: get data dst is not local");
962 }
963 } else {
964 println!("{:?} {:?} {:?}", self.size, index, data.len(),);
965 panic!("index out of bounds");
966 }
967 }
968
969 /// copy data from remote memory location into provided data buffer
970 ///
971 /// # Arguments
972 ///
973 /// * `pe` - id of remote PE to grab data from
974 /// * `index` - offset into the remote memory window
975 /// * `data` - address (which is "registered" with network device) of destination buffer to store result of the get
976 /// data will be present within the buffer once this returns.
977 //#[tracing::instrument(skip_all)]
978 pub(crate) unsafe fn blocking_get<R: Dist, U: Into<LamellarMemoryRegion<R>>>(
979 &self,
980 pe: usize,
981 index: usize,
982 data: U,
983 ) {
984 let data = data.into();
985 if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
986 let num_bytes = data.len() * std::mem::size_of::<R>();
987 if let Ok(ptr) = data.as_mut_ptr() {
988 let bytes = std::slice::from_raw_parts_mut(ptr as *mut u8, num_bytes);
989 // println!(
990 // "getting {:?} {:?} {:?} {:?} {:?} {:?} {:?}",
991 // pe,
992 // index,
993 // std::mem::size_of::<R>(),
994 // data.len(),
995 // num_bytes,
996 // self.size,
997 // self.num_bytes
998 // );
999 self.rdma
1000 .iget(pe, self.addr + index * std::mem::size_of::<R>(), bytes);
1001 //(remote pe, src, dst)
1002 // println!("getting {:?} {:?} [{:?}] {:?} {:?} {:?}",pe,self.addr + index * std::mem::size_of::<T>(),index,data.addr(),data.len(),num_bytes);
1003 } else {
1004 panic!("ERROR: get data dst is not local");
1005 }
1006 } else {
1007 println!("{:?} {:?} {:?}", self.size, index, data.len(),);
1008 panic!("index out of bounds");
1009 }
1010 }
1011
1012 //we must ensure the the slice will live long enough and that it already exsists in registered memory
1013 //#[tracing::instrument(skip_all)]
1014 pub(crate) unsafe fn put_slice<R: Dist>(&self, pe: usize, index: usize, data: &[R]) {
1015 //todo make return a result?
1016 if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
1017 let num_bytes = data.len() * std::mem::size_of::<R>();
1018 let bytes = std::slice::from_raw_parts(data.as_ptr() as *const u8, num_bytes);
1019 // println!(
1020 // "mem region len: {:?} index: {:?} data len{:?} num_bytes {:?} from {:?} to {:x} ({:x} [{:?}])",
1021 // self.size,
1022 // index,
1023 // data.len(),
1024 // num_bytes,
1025 // data.as_ptr(),
1026 // self.addr,
1027 // self.addr + index * std::mem::size_of::<T>(),
1028 // pe,
1029 // );
1030 self.rdma
1031 .put(pe, bytes, self.addr + index * std::mem::size_of::<R>())
1032 } else {
1033 println!(
1034 "mem region len: {:?} index: {:?} data len{:?}",
1035 self.size,
1036 index,
1037 data.len()
1038 );
1039 panic!("index out of bounds");
1040 }
1041 }
1042 /// copy data from remote memory location into provided data buffer
1043 ///
1044 /// # Arguments
1045 ///
1046 /// * `pe` - id of remote PE to grab data from
1047 /// * `index` - offset into the remote memory window
1048 /// * `data` - address (which is "registered" with network device) of destination buffer to store result of the get
1049 /// data will be present within the buffer once this returns.
1050 //#[tracing::instrument(skip_all)]
1051 pub(crate) unsafe fn blocking_get_slice<R: Dist>(
1052 &self,
1053 pe: usize,
1054 index: usize,
1055 data: &mut [R],
1056 ) {
1057 // let data = data.into();
1058 if (index + data.len()) * std::mem::size_of::<R>() <= self.num_bytes {
1059 let num_bytes = data.len() * std::mem::size_of::<R>();
1060 let bytes = std::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut u8, num_bytes);
1061 // println!("getting {:?} {:?} {:?} {:?} {:?} {:?} {:?}",pe,index,std::mem::size_of::<R>(),data.len(), num_bytes,self.size, self.num_bytes);
1062
1063 self.rdma
1064 .iget(pe, self.addr + index * std::mem::size_of::<R>(), bytes);
1065 //(remote pe, src, dst)
1066 // println!("getting {:?} {:?} [{:?}] {:?} {:?} {:?}",pe,self.addr + index * std::mem::size_of::<T>(),index,data.addr(),data.len(),num_bytes);
1067 } else {
1068 println!("{:?} {:?} {:?}", self.size, index, data.len(),);
1069 panic!("index out of bounds");
1070 }
1071 }
1072
1073 #[allow(dead_code)]
1074 //#[tracing::instrument(skip_all)]
1075 pub(crate) unsafe fn fill_from_remote_addr<R: Dist>(
1076 &self,
1077 my_index: usize,
1078 pe: usize,
1079 addr: usize,
1080 len: usize,
1081 ) {
1082 if (my_index + len) * std::mem::size_of::<R>() <= self.num_bytes {
1083 let num_bytes = len * std::mem::size_of::<R>();
1084 let my_offset = self.addr + my_index * std::mem::size_of::<R>();
1085 let bytes = std::slice::from_raw_parts_mut(my_offset as *mut u8, num_bytes);
1086 let local_addr = self.rdma.local_addr(pe, addr);
1087 self.rdma.iget(pe, local_addr, bytes);
1088 } else {
1089 println!(
1090 "mem region len: {:?} index: {:?} data len{:?}",
1091 self.size, my_index, len
1092 );
1093 panic!("index out of bounds");
1094 }
1095 }
1096
1097 #[allow(dead_code)]
1098 //#[tracing::instrument(skip_all)]
1099 pub(crate) fn len(&self) -> usize {
1100 self.size
1101 }
1102
1103 //#[tracing::instrument(skip_all)]
1104 pub(crate) fn addr(&self) -> MemResult<usize> {
1105 Ok(self.addr)
1106 }
1107
1108 //#[tracing::instrument(skip_all)]
1109 pub(crate) fn casted_at<R: Dist>(&self, index: usize) -> MemResult<&R> {
1110 if self.addr != 0 {
1111 let num_bytes = self.size * std::mem::size_of::<T>();
1112 assert_eq!(
1113 num_bytes % std::mem::size_of::<R>(),
1114 0,
1115 "Error converting memregion to new base, does not align"
1116 );
1117 Ok(unsafe {
1118 &std::slice::from_raw_parts(
1119 self.addr as *const R,
1120 num_bytes / std::mem::size_of::<R>(),
1121 )[index]
1122 })
1123 } else {
1124 Err(MemNotLocalError {})
1125 }
1126 }
1127
1128 //#[tracing::instrument(skip_all)]
1129 pub(crate) fn as_slice(&self) -> MemResult<&[T]> {
1130 if self.addr != 0 {
1131 Ok(unsafe { std::slice::from_raw_parts(self.addr as *const T, self.size) })
1132 } else {
1133 Ok(&[])
1134 }
1135 }
1136 //#[tracing::instrument(skip_all)]
1137 pub(crate) fn as_casted_slice<R: Dist>(&self) -> MemResult<&[R]> {
1138 if self.addr != 0 {
1139 let num_bytes = self.size * std::mem::size_of::<T>();
1140 assert_eq!(
1141 num_bytes % std::mem::size_of::<R>(),
1142 0,
1143 "Error converting memregion to new base, does not align"
1144 );
1145 Ok(unsafe {
1146 std::slice::from_raw_parts(
1147 self.addr as *const R,
1148 num_bytes / std::mem::size_of::<R>(),
1149 )
1150 })
1151 } else {
1152 Ok(&[])
1153 }
1154 }
1155 //#[tracing::instrument(skip_all)]
1156 pub(crate) unsafe fn as_mut_slice(&self) -> MemResult<&mut [T]> {
1157 if self.addr != 0 {
1158 Ok(std::slice::from_raw_parts_mut(
1159 self.addr as *mut T,
1160 self.size,
1161 ))
1162 } else {
1163 Ok(&mut [])
1164 }
1165 }
1166 //#[tracing::instrument(skip_all)]
1167 pub(crate) unsafe fn as_casted_mut_slice<R: Dist>(&self) -> MemResult<&mut [R]> {
1168 if self.addr != 0 {
1169 let num_bytes = self.size * std::mem::size_of::<T>();
1170 assert_eq!(
1171 num_bytes % std::mem::size_of::<R>(),
1172 0,
1173 "Error converting memregion to new base, does not align"
1174 );
1175 Ok(std::slice::from_raw_parts_mut(
1176 self.addr as *mut R,
1177 num_bytes / std::mem::size_of::<R>(),
1178 ))
1179 } else {
1180 Ok(&mut [])
1181 }
1182 }
1183 #[allow(dead_code)]
1184 //#[tracing::instrument(skip_all)]
1185 pub(crate) fn as_ptr(&self) -> MemResult<*const T> {
1186 Ok(self.addr as *const T)
1187 }
1188 #[allow(dead_code)]
1189 //#[tracing::instrument(skip_all)]
1190 pub(crate) fn as_casted_ptr<R: Dist>(&self) -> MemResult<*const R> {
1191 Ok(self.addr as *const R)
1192 }
1193 #[allow(dead_code)]
1194 //#[tracing::instrument(skip_all)]
1195 pub(crate) fn as_mut_ptr(&self) -> MemResult<*mut T> {
1196 Ok(self.addr as *mut T)
1197 }
1198 #[allow(dead_code)]
1199 //#[tracing::instrument(skip_all)]
1200 pub(crate) fn as_casted_mut_ptr<R: Dist>(&self) -> MemResult<*mut R> {
1201 Ok(self.addr as *mut R)
1202 }
1203}
1204
1205impl<T: Dist> MemRegionId for MemoryRegion<T> {
1206 //#[tracing::instrument(skip_all)]
1207 fn id(&self) -> usize {
1208 self.addr //probably should be key
1209 }
1210}
1211
1212/// The interface for allocating shared and onesided memory regions
1213pub trait RemoteMemoryRegion {
1214 #[doc(alias = "Collective")]
1215 /// Allocate a shared memory region from the asymmetric heap.
1216 /// There will be `size` number of `T` elements on each PE.
1217 ///
1218 /// Note: If there is not enough memory in the lamellar heap on the calling PE
1219 /// this call will trigger a "heap grow" operation (initiated and handled by the runtime),
1220 /// this behavior can be disabled by setting the env variable "LAMELLAR_HEAP_MODE=static",
1221 /// in which case this call will cause a panic if there is not enough memory.
1222 ///
1223 /// Alternatively, you can use the `try_alloc_shared_mem_region` method which returns
1224 /// a `Result` and allows you to handle the error case when there is not enough memory.
1225 ///
1226 /// # Collective Operation
1227 /// Requires all PEs associated with the `array` to enter the call otherwise deadlock will occur (i.e. team barriers are being called internally)
1228 ///
1229 fn alloc_shared_mem_region<T: Dist + std::marker::Sized>(
1230 &self,
1231 size: usize,
1232 ) -> SharedMemoryRegionHandle<T>;
1233
1234 #[doc(alias = "Collective")]
1235 /// Allocate a shared memory region from the asymmetric heap.
1236 /// There will be `size` number of `T` elements on each PE.
1237 ///
1238 /// # Collective Operation
1239 /// Requires all PEs associated with the `array` to enter the call otherwise deadlock will occur (i.e. team barriers are being called internally)
1240 ///
1241 fn try_alloc_shared_mem_region<T: Dist + std::marker::Sized>(
1242 &self,
1243 size: usize,
1244 ) -> FallibleSharedMemoryRegionHandle<T>;
1245
1246 #[doc(alias("One-sided", "onesided"))]
1247 /// Allocate a one-sided memory region from the internal lamellar heap.
1248 /// This region only exists on the calling PE, but the returned handle can be
1249 /// sent to other PEs allowing remote access to the region.
1250 /// There will be `size` number of `T` elements on the calling PE.
1251 ///
1252 /// Note: If there is not enough memory in the lamellar heap on the calling PE
1253 /// this call will trigger a "heap grow" operation (initiated and handled by the runtime),
1254 /// this behavior can be disabled by setting the env variable "LAMELLAR_HEAP_MODE=static",
1255 /// in which case this call will cause a panic if there is not enough memory.
1256 ///
1257 /// Alternatively, you can use the `try_alloc_one_sided_mem_region` method which returns
1258 /// a `Result` and allows you to handle the error case when there is not enough memory.
1259 ///
1260 /// # One-sided Operation
1261 /// the calling PE will allocate the memory region locally, without intervention from the other PEs.
1262 ///
1263 fn alloc_one_sided_mem_region<T: Dist + std::marker::Sized>(
1264 &self,
1265 size: usize,
1266 ) -> OneSidedMemoryRegion<T>;
1267
1268 #[doc(alias("One-sided", "onesided"))]
1269 /// Allocate a one-sided memory region from the internal lamellar heap.
1270 /// This region only exists on the calling PE, but the returned handle can be
1271 /// sent to other PEs allowing remote access to the region.
1272 /// There will be `size` number of `T` elements on the calling PE.
1273 ///
1274 /// # One-sided Operation
1275 /// the calling PE will allocate the memory region locally, without intervention from the other PEs.
1276 ///
1277 fn try_alloc_one_sided_mem_region<T: Dist + std::marker::Sized>(
1278 &self,
1279 size: usize,
1280 ) -> Result<OneSidedMemoryRegion<T>, anyhow::Error>;
1281}
1282
1283impl<T: Dist> Drop for MemoryRegion<T> {
1284 //#[tracing::instrument(skip_all)]
1285 fn drop(&mut self) {
1286 // println!("trying to dropping mem region {:?}",self);
1287 if self.addr != 0 {
1288 match self.mode {
1289 Mode::Local => self.rdma.rt_free(self.addr), // - self.rdma.base_addr());
1290 Mode::Shared => self.rdma.free(self.addr),
1291 Mode::Remote => {}
1292 }
1293 }
1294 // println!("dropping mem region {:?}",self);
1295 }
1296}
1297
1298impl<T: Dist> std::fmt::Debug for MemoryRegion<T> {
1299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1300 // write!(f, "{:?}", slice)
1301 write!(
1302 f,
1303 "addr {:#x} size {:?} backend {:?}", // cnt: {:?}",
1304 self.addr,
1305 self.size,
1306 self.backend,
1307 // self.cnt.load(Ordering::SeqCst)
1308 )
1309 }
1310}