lamellar/array/operations/
access.rs

1use crate::array::*;
2
3use super::handle::{
4    ArrayBatchOpHandle, ArrayFetchBatchOpHandle, ArrayFetchOpHandle, ArrayOpHandle,
5};
6
7#[doc(alias("One-sided", "onesided"))]
8/// The interface for remotely writing elements
9///
10/// These operations can be performed using any 'safe' [LamellarWriteArray]  type
11/// For UnsafeArrays please see [UnsafeAccessOps]
12///
13/// Both single element operations and batched element operations are provided
14///
15/// Generally if you are performing a large number of operations it will be better to
16/// use a batched version instead of multiple single element opertations. While the
17/// Runtime internally performs message aggregation for both single element and batched
18/// operations, single element operates have to be treated as individual requests, resulting
19/// in allocation and bookkeeping overheads. A single batched call on the other hand is treated
20/// as a single request by the runtime. (See [ReadOnlyOps] for an example comparing single vs batched load operations of a list of indices)
21///
22/// The results of a batched operation are returned to the user in the same order as the input indices.
23///
24/// # One-sided Operation
25/// performing either single or batched operations are both one-sided, with the calling PE performing any necessary work to
26/// initate and execute active messages that are sent to remote PEs.
27/// For Ops that return results, the result will only be available on the calling PE.
28///
29/// # Note
30/// For both single index and batched operations there are no guarantees to the order in which individual operations occur (an individal operation is guaranteed to be atomic though).
31///
32/// # Batched Types
33/// Three types of batched operations can be performed
34/// ## One Value - Many Indicies
35/// In this type, the same value will be applied to the provided indices
36///```
37/// use lamellar::array::prelude::*;
38///
39/// let world = LamellarWorldBuilder::new().build();
40/// let array = AtomicArray::<usize>::new(&world,100,Distribution::Block).block();
41///
42/// let indices = vec![3,54,12,88,29,68];
43/// let val = 10;
44/// array.batch_store(indices,val).block();
45///```
46/// ## Many Values - One Index
47/// In this type, multiple values will be applied to the given index
48///```
49/// use lamellar::array::prelude::*;
50///
51/// let world = LamellarWorldBuilder::new().build();
52/// let array = AtomicArray::<usize>::new(&world,100,Distribution::Block).block();
53///
54/// let vals = vec![3,54,12,88,29,68];
55/// let index = 10;
56/// array.batch_store(index,vals).block();
57///```
58/// ## Many Values - Many Indicies
59/// In this type, values and indices have a one-to-one correspondance.
60///
61/// If the two lists are unequal in length, the longer of the two will be truncated so that it matches the length of the shorter
62///```
63/// use lamellar::array::prelude::*;
64///
65/// let world = LamellarWorldBuilder::new().build();
66/// let array = AtomicArray::<usize>::new(&world,100,Distribution::Block).block();
67///
68/// let indices = vec![3,54,12,88,29,68];
69/// let vals = vec![12,2,1,10000,12,13];
70/// array.batch_store(indices,vals).block();
71///```
72pub trait AccessOps<T: ElementOps>: private::LamellarArrayPrivate<T> {
73    /// This call stores the supplied `val` into the element specified by `index`
74    ///
75    /// A future is returned as the result of this call, which is used to detect when the operation has completed
76    ///
77    /// # Note
78    /// This future is only lazy with respect to checking for completion, not
79    /// with respect to launching the operation. That is, the operation will
80    /// occur regardless of if the future is ever polled or not, Enabling
81    /// a "fire and forget" programming model.
82    ///
83    /// # Examples
84    ///
85    ///```
86    /// use lamellar::array::prelude::*;
87    ///
88    /// let world = LamellarWorldBuilder::new().build();
89    /// let array = AtomicArray::<usize>::new(&world,100,Distribution::Block).block();
90    ///
91    /// let idx = 53;
92    /// let val = 10;
93    /// let req = array.store(idx,val);
94    /// req.block();
95    ///```
96    //#[tracing::instrument(skip_all)]
97    fn store<'a>(&self, index: usize, val: T) -> ArrayOpHandle {
98        self.inner_array()
99            .initiate_batch_op(val, index, ArrayOpCmd::Store, self.as_lamellar_byte_array())
100            .into()
101    }
102
103    /// This call performs a batched vesion of the [store][AccessOps::store] function,
104    ///
105    /// Instead of a single value and index this function expects a list of `vals`, or a list of `indices` or both.
106    /// Please see the general [AccessOps] documentation for more information on batch operation input
107    ///
108    /// A future is returned as the result of this call, which is used to detect when the operation has completed
109    ///
110    /// # Note
111    /// This future is only lazy with respect to checking for completion, not
112    /// with respect to launching the operation. That is, the operation will
113    /// occur regardless of if the future is ever polled or not, Enabling
114    /// a "fire and forget" programming model.
115    ///
116    /// # Examples
117    ///
118    ///```
119    /// use lamellar::array::prelude::*;
120    ///
121    /// let world = LamellarWorldBuilder::new().build();
122    /// let array = AtomicArray::<usize>::new(&world,100,Distribution::Block).block();
123    ///
124    /// let indices = vec![3,54,12,88,29,68];
125    /// let req = array.batch_store(indices,10);
126    /// req.block();
127    ///```
128    //#[tracing::instrument(skip_all)]
129    fn batch_store<'a>(
130        &self,
131        index: impl OpInput<'a, usize>,
132        val: impl OpInput<'a, T>,
133    ) -> ArrayBatchOpHandle {
134        self.inner_array().initiate_batch_op(
135            val,
136            index,
137            ArrayOpCmd::Store,
138            self.as_lamellar_byte_array(),
139        )
140    }
141
142    /// This call swaps the supplied `val` into the element specified by `index`, returning the old value
143    ///
144    /// A future is returned as the result of this call, which is used to retrieve
145    /// the results after the (possibly remote) operations have finished.
146    ///
147    /// # Note
148    /// This future is only lazy with respect to retrieving the result, not
149    /// with respect to launching the operation. That is, the operation will
150    /// occur regardless of if the future is ever polled or not, Enabling
151    /// a "fire and forget" programming model.
152    ///
153    /// # Examples
154    ///
155    ///```
156    /// use lamellar::array::prelude::*;
157    ///
158    /// let world = LamellarWorldBuilder::new().build();
159    /// let array = AtomicArray::<usize>::new(&world,100,Distribution::Block).block();
160    ///
161    /// let idx = 53;
162    /// let new = 10;
163    /// let req = array.swap(idx,new);
164    /// let old = req.block();
165    ///```
166    //#[tracing::instrument(skip_all)]
167    fn swap<'a>(&self, index: usize, val: T) -> ArrayFetchOpHandle<T> {
168        self.inner_array()
169            .initiate_batch_fetch_op_2(val, index, ArrayOpCmd::Swap, self.as_lamellar_byte_array())
170            .into()
171    }
172
173    /// This call performs a batched vesion of the [swap][AccessOps::swap] function,
174    ///
175    /// Instead of a single value and index this function expects a list of `vals`, or a list of `indices` or both.
176    /// Please see the general [AccessOps] documentation for more information on batch operation input
177    ///
178    /// A future is returned as the result of this call, which is used to retrieve
179    /// the results after the (possibly remote) operations have finished.
180    ///
181    /// # Note
182    /// This future is only lazy with respect to checking for completion, not
183    /// with respect to launching the operation. That is, the operation will
184    /// occur regardless of if the future is ever polled or not, Enabling
185    /// a "fire and forget" programming model.
186    ///
187    /// # Examples
188    ///
189    ///```
190    /// use lamellar::array::prelude::*;
191    ///
192    /// let world = LamellarWorldBuilder::new().build();
193    /// let array = AtomicArray::<usize>::new(&world,100,Distribution::Block).block();
194    ///
195    /// let indices = vec![3,54,12,88,29,68];
196    /// let req = array.batch_swap(indices,10);
197    /// let old_vals = req.block();
198    ///```
199    //#[tracing::instrument(skip_all)]
200    fn batch_swap<'a>(
201        &self,
202        index: impl OpInput<'a, usize>,
203        val: impl OpInput<'a, T>,
204    ) -> ArrayFetchBatchOpHandle<T> {
205        self.inner_array().initiate_batch_fetch_op_2(
206            val,
207            index,
208            ArrayOpCmd::Swap,
209            self.as_lamellar_byte_array(),
210        )
211    }
212}
213
214#[doc(alias("One-sided", "onesided"))]
215/// The interface for remotely writing elements on [UnsafeArray]s
216///
217/// Both single element operations and batched element operations are provided
218///
219/// Generally if you are performing a large number of operations it will be better to
220/// use a batched version instead of multiple single element opertations. While the
221/// Runtime internally performs message aggregation for both single element and batched
222/// operations, single element operates have to be treated as individual requests, resulting
223/// in allocation and bookkeeping overheads. A single batched call on the other hand is treated
224/// as a single request by the runtime. (See [ReadOnlyOps] for an example comparing single vs batched load operations of a list of indices)
225///
226/// The results of a batched operation are returned to the user in the same order as the input indices.
227///
228/// # One-sided Operation
229/// performing either single or batched operations are both one-sided, with the calling PE performing any necessary work to
230/// initate and execute active messages that are sent to remote PEs.
231/// For Ops that return results, the result will only be available on the calling PE.
232///
233/// # Note
234/// For both single index and batched operations there are no guarantees to the order in which individual operations occur.
235///
236/// # Batched Types
237/// Three types of batched operations can be performed
238/// ## One Value - Many Indicies
239/// In this type, the same value will be applied to the provided indices
240///```
241/// use lamellar::array::prelude::*;
242///
243/// let world = LamellarWorldBuilder::new().build();
244/// let array = UnsafeArray::<usize>::new(&world,100,Distribution::Block).block();
245///
246/// let indices = vec![3,54,12,88,29,68];
247/// let val = 10;
248/// unsafe{array.batch_store(indices,val).block()};
249///```
250/// ## Many Values - One Index
251/// In this type, multiple values will be applied to the given index
252///```
253/// use lamellar::array::prelude::*;
254///
255/// let world = LamellarWorldBuilder::new().build();
256/// let array = UnsafeArray::<usize>::new(&world,100,Distribution::Block).block();
257///
258/// let vals = vec![3,54,12,88,29,68];
259/// let index = 10;
260/// unsafe{array.batch_store(index,vals).block()};
261///```
262/// ## Many Values - Many Indicies
263/// In this type, values and indices have a one-to-one correspondance.
264///
265/// If the two lists are unequal in length, the longer of the two will be truncated so that it matches the length of the shorter
266///```
267/// use lamellar::array::prelude::*;
268///
269/// let world = LamellarWorldBuilder::new().build();
270/// let array = UnsafeArray::<usize>::new(&world,100,Distribution::Block).block();
271///
272/// let indices = vec![3,54,12,88,29,68];
273/// let vals = vec![12,2,1,10000,12,13];
274/// unsafe{array.batch_store(indices,vals).block()};
275///```
276pub trait UnsafeAccessOps<T: ElementOps>: private::LamellarArrayPrivate<T> {
277    /// This call stores the supplied `val` into the element specified by `index`
278    ///
279    /// A future is returned as the result of this call, which is used to detect when the operation has completed
280    ///
281    /// # Note
282    /// This future is only lazy with respect to checking for completion, not
283    /// with respect to launching the operation. That is, the operation will
284    /// occur regardless of if the future is ever polled or not, Enabling
285    /// a "fire and forget" programming model.
286    ///
287    /// # Examples
288    ///
289    ///```
290    /// use lamellar::array::prelude::*;
291    ///
292    /// let world = LamellarWorldBuilder::new().build();
293    /// let array = UnsafeArray::<usize>::new(&world,100,Distribution::Block).block();
294    ///
295    /// let idx = 53;
296    /// let val = 10;
297    /// let req = unsafe{array.store(idx,val)};
298    /// req.block();
299    ///```
300    //#[tracing::instrument(skip_all)]
301    unsafe fn store<'a>(&self, index: usize, val: T) -> ArrayOpHandle {
302        self.inner_array()
303            .initiate_batch_op(val, index, ArrayOpCmd::Store, self.as_lamellar_byte_array())
304            .into()
305    }
306
307    /// This call performs a batched vesion of the [store][AccessOps::store] function,
308    ///
309    /// Instead of a single value and index this function expects a list of `vals`, or a list of `indices` or both.
310    /// Please see the general [AccessOps] documentation for more information on batch operation input
311    ///
312    /// A future is returned as the result of this call, which is used to detect when the operation has completed
313    ///
314    /// # Note
315    /// This future is only lazy with respect to checking for completion, not
316    /// with respect to launching the operation. That is, the operation will
317    /// occur regardless of if the future is ever polled or not, Enabling
318    /// a "fire and forget" programming model.
319    ///
320    /// # Examples
321    ///
322    ///```
323    /// use lamellar::array::prelude::*;
324    ///
325    /// let world = LamellarWorldBuilder::new().build();
326    /// let array = UnsafeArray::<usize>::new(&world,100,Distribution::Block).block();
327    ///
328    /// let indices = vec![3,54,12,88,29,68];
329    /// let req = unsafe{array.batch_store(indices,10)};
330    /// req.block();
331    ///```
332    //#[tracing::instrument(skip_all)]
333    unsafe fn batch_store<'a>(
334        &self,
335        index: impl OpInput<'a, usize>,
336        val: impl OpInput<'a, T>,
337    ) -> ArrayBatchOpHandle {
338        self.inner_array().initiate_batch_op(
339            val,
340            index,
341            ArrayOpCmd::Store,
342            self.as_lamellar_byte_array(),
343        )
344    }
345
346    /// This call swaps the supplied `val` into the element specified by `index`, returning the old value
347    ///
348    /// A future is returned as the result of this call, which is used to retrieve
349    /// the results after the (possibly remote) operations have finished.
350    ///
351    /// # Note
352    /// This future is only lazy with respect to retrieving the result, not
353    /// with respect to launching the operation. That is, the operation will
354    /// occur regardless of if the future is ever polled or not, Enabling
355    /// a "fire and forget" programming model.
356    ///
357    /// # Examples
358    ///
359    ///```
360    /// use lamellar::array::prelude::*;
361    ///
362    /// let world = LamellarWorldBuilder::new().build();
363    /// let array = UnsafeArray::<usize>::new(&world,100,Distribution::Block).block();
364    ///
365    /// let idx = 53;
366    /// let new = 10;
367    /// let req = unsafe{array.swap(idx,new)};
368    /// let old = req.block();
369    ///```
370    //#[tracing::instrument(skip_all)]
371    unsafe fn swap<'a>(&self, index: usize, val: T) -> ArrayFetchOpHandle<T> {
372        self.inner_array()
373            .initiate_batch_fetch_op_2(val, index, ArrayOpCmd::Swap, self.as_lamellar_byte_array())
374            .into()
375    }
376
377    /// This call performs a batched vesion of the [swap][AccessOps::swap] function,
378    ///
379    /// Instead of a single value and index this function expects a list of `vals`, or a list of `indices` or both.
380    /// Please see the general [AccessOps] documentation for more information on batch operation input
381    ///
382    /// A future is returned as the result of this call, which is used to retrieve
383    /// the results after the (possibly remote) operations have finished.
384    ///
385    /// # Note
386    /// This future is only lazy with respect to checking for completion, not
387    /// with respect to launching the operation. That is, the operation will
388    /// occur regardless of if the future is ever polled or not, Enabling
389    /// a "fire and forget" programming model.
390    ///
391    /// # Examples
392    ///
393    ///```
394    /// use lamellar::array::prelude::*;
395    ///
396    /// let world = LamellarWorldBuilder::new().build();
397    /// let array = UnsafeArray::<usize>::new(&world,100,Distribution::Block).block();
398    ///
399    /// let indices = vec![3,54,12,88,29,68];
400    /// let req = unsafe{array.batch_swap(indices,10)};
401    /// let old_vals = req.block();
402    ///```
403    //#[tracing::instrument(skip_all)]
404    unsafe fn batch_swap<'a>(
405        &self,
406        index: impl OpInput<'a, usize>,
407        val: impl OpInput<'a, T>,
408    ) -> ArrayFetchBatchOpHandle<T> {
409        self.inner_array().initiate_batch_fetch_op_2(
410            val,
411            index,
412            ArrayOpCmd::Swap,
413            self.as_lamellar_byte_array(),
414        )
415    }
416}
417
418#[doc(hidden)]
419pub trait LocalAtomicOps<T: Dist + ElementOps> {
420    fn local_load(&self, index: usize, val: T) -> T;
421    fn local_store(&self, index: usize, val: T);
422    fn local_swap(&self, index: usize, val: T) -> T;
423}