Skip to main content

lamellar/array/iterator/
mod.rs

1//! Provides various iterator types for LamellarArrays
2pub mod distributed_iterator;
3use std::pin::Pin;
4
5use distributed_iterator::DistributedIterator;
6pub mod local_iterator;
7use local_iterator::LocalIterator;
8pub mod one_sided_iterator;
9use one_sided_iterator::OneSidedIterator;
10pub mod consumer;
11
12use crate::memregion::Dist;
13
14// //#[doc(hidden)]
15// #[async_trait]
16// pub trait IterRequest {
17//     type Output;
18//     async fn into_future(mut self: Box<Self>) -> Self::Output;
19//     fn wait(self: Box<Self>) -> Self::Output;
20// }
21
22pub(crate) type IterLockFuture = Pin<Box<dyn std::future::Future<Output = ()> + Send>>;
23pub(crate) mod private {
24    use super::IterLockFuture;
25
26    #[derive(Debug, Clone, Copy)]
27    pub struct Sealed;
28
29    pub trait InnerIter: Sized {
30        fn lock_if_needed(&self, _s: Sealed) -> Option<IterLockFuture>;
31        fn iter_clone(&self, _s: Sealed) -> Self;
32    }
33}
34
35/// The Schedule type controls how elements of a LamellarArray are distributed to threads when
36/// calling `for_each_with_schedule` on a local or distributed iterator.
37///
38/// Inspired by then OpenMP schedule parameter
39///
40/// # Possible Options
41/// - Static: Each thread recieves a static range of elements to iterate over, the range length is roughly array.local_data().len()/number of threads on pe
42/// - Dynaimc: Each thread processes a single element at a time
43/// - Chunk(usize): Each thread prcesses chunk sized range of elements at a time.
44/// - Guided: Similar to chunks, but the chunks decrease in size over time
45/// - WorkStealing: Intially allocated the same range as static, but allows idle threads to steal work from busy threads
46#[derive(Debug, Clone)]
47pub enum Schedule {
48    /// local_data.len()/number of threads elements per thread
49    Static,
50    ///single element at a time
51    Dynamic,
52    ///dynamic but with multiple elements
53    Chunk(usize),
54    /// chunks that get smaller over time
55    Guided,
56    /// static initially but other threads can steal
57    WorkStealing,
58}
59
60/// The interface for creating the various lamellar array iterator types
61///
62/// This is only implemented for Safe Array types, [UnsafeArray][crate::array::unsafe::UnsafeArray] directly provides unsafe versions of the same functions
63pub trait LamellarArrayIterators<T: Dist> {
64    /// The [DistributedIterator] type
65    type DistIter: DistributedIterator;
66    /// The [LocalIterator] type
67    type LocalIter: LocalIterator;
68    /// The [OneSidedIterator] type
69    type OnesidedIter: OneSidedIterator;
70
71    #[doc(alias = "Collective")]
72    /// Create an immutable [DistributedIterator] for this array
73    ///
74    /// # Collective Operation
75    /// Requires all PEs associated with the array to enter the call otherwise deadlock will occur (i.e. barriers are being called internally)
76    /// Throughout execution of the iteration, data movement may occur amongst various PEs
77    ///
78    /// # Examples
79    ///```
80    /// use lamellar::array::prelude::*;
81    /// let world = LamellarWorldBuilder::new().build();
82    /// let my_pe = world.my_pe();
83    /// let array: ReadOnlyArray<usize> = ReadOnlyArray::new(&world,100,Distribution::Cyclic).block();
84    ///
85    ///
86    ///     array.dist_iter().for_each(move |elem| println!("PE{my_pe} elem {elem}"))
87    /// .block();
88    ///```
89    fn dist_iter(&self) -> Self::DistIter;
90
91    #[doc(alias("One-sided", "onesided"))]
92    /// Create an immutable [LocalIterator] for this array
93    ///
94    /// # One-sided Operation
95    /// The iteration is launched and local to only the calling PE.
96    /// No data movement from remote PEs is required
97    ///
98    /// # Examples
99    ///```
100    /// use lamellar::array::prelude::*;
101    /// let world = LamellarWorldBuilder::new().build();
102    /// let my_pe = world.my_pe();
103    /// let array: AtomicArray<usize> = AtomicArray::new(&world,100,Distribution::Cyclic).block();
104    ///
105    ///
106    ///     array.local_iter().for_each(move |elem| println!("PE{my_pe} elem {}",elem.load())) // "load" is specific to AtomicArray elements, other types can deref the element directly"
107    /// .block();
108    ///```
109    fn local_iter(&self) -> Self::LocalIter;
110
111    #[doc(alias("One-sided", "onesided"))]
112    /// Create an immutable [OneSidedIterator] for this array
113    ///
114    /// # One-sided Operation
115    /// The iteration is launched and local to only the calling PE.
116    /// Data movement will occur with the remote PEs to transfer their data to the calling PE
117    ///
118    /// # Examples
119    ///```
120    /// use lamellar::array::prelude::*;
121    /// let world = LamellarWorldBuilder::new().build();
122    /// let my_pe = world.my_pe();
123    /// let array: ReadOnlyArray<usize> = ReadOnlyArray::new(&world,100,Distribution::Cyclic).block();
124    ///
125    /// if my_pe == 0 {
126    ///     for elem in array.onesided_iter().into_iter() { //"into_iter()" converts into a standard Rust Iterator
127    ///         println!("PE{my_pe} elem {elem}");
128    ///     }
129    /// }
130    ///```
131    fn onesided_iter(&self) -> Self::OnesidedIter;
132
133    #[doc(alias("One-sided", "onesided"))]
134    /// Create an immutable [OneSidedIterator]  for this array
135    /// which will transfer and buffer `buf_size` elements at a time (to more efficient utilize the underlying lamellae network)
136    ///
137    /// The buffering is transparent to the user.
138    ///
139    /// This iterator typcially outperforms the non buffered version.
140    ///
141    /// # One-sided Operation
142    /// The iteration is launched and local to only the calling PE.
143    /// Data movement will occur with the remote PEs to transfer their data to the calling PE
144    ///
145    /// # Examples
146    ///```
147    /// use lamellar::array::prelude::*;
148    /// let world = LamellarWorldBuilder::new().build();
149    /// let my_pe = world.my_pe();
150    /// let array: LocalLockArray<usize> = LocalLockArray::new(&world,100,Distribution::Cyclic).block();
151    ///
152    /// if my_pe == 0 {
153    ///     for elem in array.buffered_onesided_iter(100).into_iter() { // "into_iter()" converts into a standard Rust Iterator
154    ///         println!("PE{my_pe} elem {elem}");
155    ///     }
156    /// }
157    ///```
158    fn buffered_onesided_iter(&self, buf_size: usize) -> Self::OnesidedIter;
159}
160
161/// The interface for creating the various lamellar array mutable iterator types
162///
163/// This is only implemented for Safe Array types, [UnsafeArray][crate::array::unsafe::UnsafeArray] directly provides unsafe versions of the same functions
164pub trait LamellarArrayMutIterators<T: Dist> {
165    /// The [DistributedIterator] type
166    type DistIter: DistributedIterator;
167    /// The [LocalIterator]type
168    type LocalIter: LocalIterator;
169
170    #[doc(alias = "Collective")]
171    /// Create a mutable [DistributedIterator] for this array
172    ///
173    /// # Collective Operation
174    /// Requires all PEs associated with the array to enter the call otherwise deadlock will occur (i.e. barriers are being called internally)
175    /// Throughout execution of the iteration, data movement may occur amongst various PEs
176    ///
177    /// # Examples
178    ///```
179    /// use lamellar::array::prelude::*;
180    /// let world = LamellarWorldBuilder::new().build();
181    /// let my_pe = world.my_pe();
182    /// let array: LocalLockArray<usize> = LocalLockArray::new(&world,100,Distribution::Cyclic).block();
183    ///
184    ///
185    ///     array.dist_iter_mut().for_each(move |elem| *elem = my_pe)
186    /// .block();
187    ///```
188    fn dist_iter_mut(&self) -> Self::DistIter;
189
190    #[doc(alias("One-sided", "onesided"))]
191    /// Create a mutable [LocalIterator] for this array
192    ///
193    /// # One-sided Operation
194    /// The iteration is launched and local to only the calling PE.
195    /// No data movement from remote PEs is required
196    ///
197    /// # Examples
198    ///```
199    /// use lamellar::array::prelude::*;
200    /// let world = LamellarWorldBuilder::new().build();
201    /// let my_pe = world.my_pe();
202    /// let array: LocalLockArray<usize> = LocalLockArray::new(&world,100,Distribution::Cyclic).block();
203    ///
204    ///
205    ///    array.local_iter_mut().for_each(move |elem| *elem = my_pe)
206    /// .block();
207    fn local_iter_mut(&self) -> Self::LocalIter;
208}