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}