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}