use crate::barrier::BarrierHandle;
use crate::darc::Darc;
use crate::lamellar_env::LamellarEnv;
use crate::memregion::{
one_sided::OneSidedMemoryRegion, shared::SharedMemoryRegion, AsLamellarBuffer, Dist,
LamellarBuffer, LamellarMemoryRegion, MemregionRdmaInputInner,
};
use crate::scheduler::LamellarTask;
use crate::{active_messaging::*, LamellarTeam, LamellarTeamRT};
use async_trait::async_trait;
use enum_dispatch::enum_dispatch;
use futures_util::Future;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::atomic::Ordering;
use std::sync::Arc;
pub use lamellar_impl::ArrayOps;
pub mod prelude;
pub(crate) mod r#unsafe;
pub use r#unsafe::{
local_chunks::{UnsafeLocalChunks, UnsafeLocalChunksMut},
operations::{
multi_val_multi_idx_ops, multi_val_multi_idx_ops_new, multi_val_single_idx_ops,
multi_val_single_idx_ops_new, single_val_multi_idx_ops, single_val_multi_idx_ops_new,
BatchReturnType,
},
UnsafeArray, __UnsafeByteArray,
};
pub(crate) mod read_only;
pub use read_only::{ReadOnlyArray, ReadOnlyLocalChunks, __ReadOnlyByteArray};
pub(crate) mod atomic;
pub use atomic::{AtomicArray, AtomicLocalData, __AtomicByteArray};
pub(crate) mod generic_atomic;
pub use generic_atomic::{GenericAtomicArray, __GenericAtomicByteArray, __GenericAtomicLocalData};
pub(crate) mod native_atomic;
pub use native_atomic::{NativeAtomicArray, __NativeAtomicByteArray, __NativeAtomicLocalData};
pub(crate) mod network_atomic;
pub use network_atomic::{NetworkAtomicArray, __NetworkAtomicByteArray, __NetworkAtomicLocalData};
pub(crate) mod local_lock_atomic;
pub use local_lock_atomic::{
LocalLockArray, LocalLockLocalChunks, LocalLockLocalChunksMut, LocalLockLocalData,
LocalLockMutLocalData, LocalLockReadGuard, LocalLockWriteGuard, __LocalLockByteArray,
};
pub(crate) mod global_lock_atomic;
pub use global_lock_atomic::{
GlobalLockArray, GlobalLockLocalData, GlobalLockMutLocalData, GlobalLockReadGuard,
GlobalLockWriteGuard, __GlobalLockByteArray,
};
pub mod iterator;
pub use iterator::distributed_iterator::DistributedIterator;
pub use iterator::local_iterator::LocalIterator;
pub use iterator::one_sided_iterator::OneSidedIterator;
pub(crate) mod operations;
pub use operations::*;
pub(crate) mod scalar_one_sided_reduce;
pub use scalar_one_sided_reduce::*;
pub(crate) mod handle;
pub use handle::*;
pub(crate) mod rdma;
use rdma::private::{LamellarRdmaGet, LamellarRdmaPut, Sealed};
pub use rdma::*;
pub(crate) mod collective;
pub(crate) type ReduceGen = fn(LamellarByteArray, usize) -> LamellarArcAm;
lazy_static! {
pub(crate) static ref REDUCE_OPS: HashMap<(std::any::TypeId, &'static str), ReduceGen> = {
let mut temp = HashMap::new();
for reduction_type in crate::inventory::iter::<ReduceKey> {
temp.insert(
((reduction_type.id)(), reduction_type.name),
reduction_type.gen,
);
}
temp
};
}
type ReduceIdGen = fn() -> std::any::TypeId;
#[doc(hidden)]
pub struct ReduceKey {
pub id: ReduceIdGen,
pub name: &'static str,
pub gen: ReduceGen,
}
crate::inventory::collect!(ReduceKey);
#[doc(hidden)]
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
#[allow(non_camel_case_types)]
pub(crate) enum ScalarType {
u8,
u16,
u32,
u64,
usize,
u128,
i8,
i16,
i32,
i64,
isize,
i128,
f32,
f64,
bool,
}
impl ScalarType {
pub(crate) fn get_type<T: 'static>() -> Option<(Self, bool)> {
match std::any::TypeId::of::<T>() {
id if id == std::any::TypeId::of::<u8>() => Some((ScalarType::u8, false)),
id if id == std::any::TypeId::of::<u16>() => Some((ScalarType::u16, false)),
id if id == std::any::TypeId::of::<u32>() => Some((ScalarType::u32, false)),
id if id == std::any::TypeId::of::<u64>() => Some((ScalarType::u64, false)),
id if id == std::any::TypeId::of::<usize>() => Some((ScalarType::usize, false)),
id if id == std::any::TypeId::of::<u128>() => Some((ScalarType::u128, false)),
id if id == std::any::TypeId::of::<i8>() => Some((ScalarType::i8, false)),
id if id == std::any::TypeId::of::<i16>() => Some((ScalarType::i16, false)),
id if id == std::any::TypeId::of::<i32>() => Some((ScalarType::i32, false)),
id if id == std::any::TypeId::of::<i64>() => Some((ScalarType::i64, false)),
id if id == std::any::TypeId::of::<isize>() => Some((ScalarType::isize, false)),
id if id == std::any::TypeId::of::<i128>() => Some((ScalarType::i128, false)),
id if id == std::any::TypeId::of::<f32>() => Some((ScalarType::f32, false)),
id if id == std::any::TypeId::of::<f64>() => Some((ScalarType::f64, false)),
id if id == std::any::TypeId::of::<bool>() => Some((ScalarType::bool, false)),
id if id == std::any::TypeId::of::<Option<u8>>() => Some((ScalarType::u8, true)),
id if id == std::any::TypeId::of::<Option<u16>>() => Some((ScalarType::u16, true)),
id if id == std::any::TypeId::of::<Option<u32>>() => Some((ScalarType::u32, true)),
id if id == std::any::TypeId::of::<Option<u64>>() => Some((ScalarType::u64, true)),
id if id == std::any::TypeId::of::<Option<usize>>() => Some((ScalarType::usize, true)),
id if id == std::any::TypeId::of::<Option<u128>>() => Some((ScalarType::u128, true)),
id if id == std::any::TypeId::of::<Option<i8>>() => Some((ScalarType::i8, true)),
id if id == std::any::TypeId::of::<Option<i16>>() => Some((ScalarType::i16, true)),
id if id == std::any::TypeId::of::<Option<i32>>() => Some((ScalarType::i32, true)),
id if id == std::any::TypeId::of::<Option<i64>>() => Some((ScalarType::i64, true)),
id if id == std::any::TypeId::of::<Option<isize>>() => Some((ScalarType::isize, true)),
id if id == std::any::TypeId::of::<Option<i128>>() => Some((ScalarType::i128, true)),
id if id == std::any::TypeId::of::<Option<f32>>() => Some((ScalarType::f32, true)),
id if id == std::any::TypeId::of::<Option<f64>>() => Some((ScalarType::f64, true)),
id if id == std::any::TypeId::of::<Option<bool>>() => Some((ScalarType::bool, true)),
_ => None,
}
}
}
impl<T: Dist + ArrayOps> Dist for Option<T> {}
impl<T: Dist + ArrayOps> ArrayOps for Option<T> {}
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug, Eq, PartialEq)]
pub enum Distribution {
Block,
Cyclic,
}
#[doc(hidden)]
#[derive(Hash, std::cmp::PartialEq, std::cmp::Eq, Clone)]
pub enum ArrayRdmaCmd {
Put,
PutAm,
Get(bool), GetAm,
}
#[derive(Clone, Debug)]
pub enum LamellarArrayRdmaInput<T: Dist> {
LamellarMemRegion(LamellarMemoryRegion<T>),
SharedMemRegion(SharedMemoryRegion<T>), LocalMemRegion(OneSidedMemoryRegion<T>),
Owned(T),
OwnedVec(Vec<T>),
}
impl<T: Dist> LamellarArrayRdmaInput<T> {
}
impl<T: Dist> LamellarRead for LamellarArrayRdmaOutput<T> {}
#[derive(Clone, Debug)]
pub enum LamellarArrayRdmaOutput<T: Dist> {
LamellarMemRegion(LamellarMemoryRegion<T>),
SharedMemRegion(SharedMemoryRegion<T>), LocalMemRegion(OneSidedMemoryRegion<T>),
}
impl<T: Dist> LamellarWrite for LamellarArrayRdmaOutput<T> {}
pub trait LamellarWrite {}
pub trait LamellarRead {}
impl<T: Dist> LamellarRead for &T {}
impl<T: Dist> LamellarRead for Vec<T> {}
impl<T: Dist> LamellarRead for &Vec<T> {}
impl<T: Dist> LamellarRead for &[T] {}
impl<T: Dist> TeamFrom<&T> for LamellarArrayRdmaInput<T> {
fn team_from(val: &T, team: &Arc<LamellarTeam>) -> Self {
let buf: OneSidedMemoryRegion<T> = team.team.alloc_one_sided_mem_region(1);
unsafe {
buf.as_mut_slice()[0] = val.clone();
}
LamellarArrayRdmaInput::LocalMemRegion(buf)
}
}
impl<T: Dist> TeamFrom<T> for LamellarArrayRdmaInput<T> {
fn team_from(val: T, _team: &Arc<LamellarTeam>) -> Self {
LamellarArrayRdmaInput::Owned(val)
}
}
impl<T: Dist> TeamFrom<Vec<T>> for LamellarArrayRdmaInput<T> {
fn team_from(vals: Vec<T>, _team: &Arc<LamellarTeam>) -> Self {
LamellarArrayRdmaInput::OwnedVec(vals)
}
}
impl<T: Dist> TeamFrom<&Vec<T>> for LamellarArrayRdmaInput<T> {
fn team_from(vals: &Vec<T>, team: &Arc<LamellarTeam>) -> Self {
let buf: OneSidedMemoryRegion<T> = team.team.alloc_one_sided_mem_region(vals.len());
unsafe {
std::ptr::copy_nonoverlapping(
vals.as_ptr(),
buf.as_mut_ptr().expect("Data should exist on PE"),
vals.len(),
);
}
LamellarArrayRdmaInput::LocalMemRegion(buf)
}
}
impl<T: Dist> TeamFrom<&[T]> for LamellarArrayRdmaInput<T> {
fn team_from(vals: &[T], team: &Arc<LamellarTeam>) -> Self {
let buf: OneSidedMemoryRegion<T> = team.team.alloc_one_sided_mem_region(vals.len());
unsafe {
std::ptr::copy_nonoverlapping(
vals.as_ptr(),
buf.as_mut_ptr().expect("Data should exist on PE"),
vals.len(),
);
}
LamellarArrayRdmaInput::LocalMemRegion(buf)
}
}
impl<T: Dist> TeamFrom<&LamellarArrayRdmaInput<T>> for LamellarArrayRdmaInput<T> {
fn team_from(lai: &LamellarArrayRdmaInput<T>, _team: &Arc<LamellarTeam>) -> Self {
lai.clone()
}
}
impl<T: Dist> TeamFrom<&LamellarArrayRdmaOutput<T>> for LamellarArrayRdmaOutput<T> {
fn team_from(lao: &LamellarArrayRdmaOutput<T>, _team: &Arc<LamellarTeam>) -> Self {
lao.clone()
}
}
impl<T: Clone> TeamFrom<(&Vec<T>, Distribution)> for Vec<T> {
fn team_from(vals: (&Vec<T>, Distribution), _team: &Arc<LamellarTeam>) -> Self {
vals.0.to_vec()
}
}
impl<T: Clone> TeamFrom<(Vec<T>, Distribution)> for Vec<T> {
fn team_from(vals: (Vec<T>, Distribution), _team: &Arc<LamellarTeam>) -> Self {
vals.0.to_vec()
}
}
impl<T: Dist> TeamTryFrom<T> for LamellarArrayRdmaInput<T> {
fn team_try_from(val: T, team: &Arc<LamellarTeam>) -> Result<Self, anyhow::Error> {
Ok(LamellarArrayRdmaInput::team_from(val, team))
}
}
impl<T: Dist> TeamTryFrom<&T> for LamellarArrayRdmaInput<T> {
fn team_try_from(val: &T, team: &Arc<LamellarTeam>) -> Result<Self, anyhow::Error> {
Ok(LamellarArrayRdmaInput::team_from(val, team))
}
}
impl<T: Dist> TeamTryFrom<Vec<T>> for LamellarArrayRdmaInput<T> {
fn team_try_from(val: Vec<T>, team: &Arc<LamellarTeam>) -> Result<Self, anyhow::Error> {
if val.len() == 0 {
Err(anyhow::anyhow!(
"Trying to create an empty LamellarArrayRdmaInput"
))
} else {
Ok(LamellarArrayRdmaInput::team_from(val, team))
}
}
}
impl<T: Dist> TeamTryFrom<&Vec<T>> for LamellarArrayRdmaInput<T> {
fn team_try_from(val: &Vec<T>, team: &Arc<LamellarTeam>) -> Result<Self, anyhow::Error> {
if val.len() == 0 {
Err(anyhow::anyhow!(
"Trying to create an empty LamellarArrayRdmaInput"
))
} else {
Ok(LamellarArrayRdmaInput::team_from(val, team))
}
}
}
impl<T: Dist> TeamTryFrom<&[T]> for LamellarArrayRdmaInput<T> {
fn team_try_from(val: &[T], team: &Arc<LamellarTeam>) -> Result<Self, anyhow::Error> {
if val.len() == 0 {
Err(anyhow::anyhow!(
"Trying to create an empty LamellarArrayRdmaInput"
))
} else {
Ok(LamellarArrayRdmaInput::team_from(val, team))
}
}
}
impl<T: Dist> TeamTryFrom<&LamellarArrayRdmaInput<T>> for LamellarArrayRdmaInput<T> {
fn team_try_from(
lai: &LamellarArrayRdmaInput<T>,
_team: &Arc<LamellarTeam>,
) -> Result<Self, anyhow::Error> {
Ok(lai.clone())
}
}
impl<T: Dist> TeamTryFrom<&LamellarArrayRdmaOutput<T>> for LamellarArrayRdmaOutput<T> {
fn team_try_from(
lao: &LamellarArrayRdmaOutput<T>,
_team: &Arc<LamellarTeam>,
) -> Result<Self, anyhow::Error> {
Ok(lao.clone())
}
}
impl<T: Clone> TeamTryFrom<(&Vec<T>, Distribution)> for Vec<T> {
fn team_try_from(
vals: (&Vec<T>, Distribution),
_team: &Arc<LamellarTeam>,
) -> Result<Self, anyhow::Error> {
Ok(vals.0.to_vec())
}
}
impl<T: Dist + ArrayOps> AsyncTeamFrom<(Vec<T>, Distribution)> for Vec<T> {
async fn team_from(input: (Vec<T>, Distribution), _team: &Arc<LamellarTeam>) -> Self {
input.0
}
}
#[async_trait]
pub(crate) trait AsyncInto<T>: Sized {
async fn async_into(self) -> T;
}
#[async_trait]
pub(crate) trait AsyncFrom<T>: Sized {
async fn async_from(val: T) -> Self;
}
#[async_trait]
impl<T, U> AsyncInto<U> for T
where
T: Send,
U: AsyncFrom<T>,
{
#[inline]
async fn async_into(self) -> U {
U::async_from(self).await
}
}
pub trait TeamFrom<T: ?Sized> {
fn team_from(val: T, team: &Arc<LamellarTeam>) -> Self;
}
pub trait AsyncTeamFrom<T: ?Sized>: Sized {
fn team_from(val: T, team: &Arc<LamellarTeam>) -> impl Future<Output = Self> + Send;
}
pub trait TeamTryFrom<T: ?Sized> {
fn team_try_from(val: T, team: &Arc<LamellarTeam>) -> Result<Self, anyhow::Error>
where
Self: Sized;
}
pub trait TeamInto<T: ?Sized> {
fn team_into(self, team: &Arc<LamellarTeam>) -> T;
}
#[async_trait]
pub trait AsyncTeamInto<T: ?Sized> {
async fn team_into(self, team: &Arc<LamellarTeam>) -> T;
}
pub trait TeamTryInto<T>: Sized {
fn team_try_into(self, team: &Arc<LamellarTeam>) -> Result<T, anyhow::Error>;
}
impl<T, U> TeamInto<U> for T
where
U: TeamFrom<T>,
{
fn team_into(self, team: &Arc<LamellarTeam>) -> U {
U::team_from(self, team)
}
}
#[async_trait]
impl<T: Send, U> AsyncTeamInto<U> for T
where
U: AsyncTeamFrom<T>,
{
async fn team_into(self, team: &Arc<LamellarTeam>) -> U {
<U as AsyncTeamFrom<T>>::team_from(self, team).await
}
}
impl<T, U> TeamTryInto<U> for T
where
U: TeamTryFrom<T>,
{
fn team_try_into(self, team: &Arc<LamellarTeam>) -> Result<U, anyhow::Error> {
U::team_try_from(self, team)
}
}
#[enum_dispatch]
#[derive(serde::Serialize, serde::Deserialize, Clone)]
#[serde(bound = "T: Dist + serde::Serialize + serde::de::DeserializeOwned + 'static")]
pub enum LamellarReadArray<T: Dist + 'static> {
UnsafeArray(UnsafeArray<T>),
ReadOnlyArray(ReadOnlyArray<T>),
AtomicArray(AtomicArray<T>),
LocalLockArray(LocalLockArray<T>),
GlobalLockArray(GlobalLockArray<T>),
}
#[doc(hidden)]
#[enum_dispatch]
#[derive(serde::Serialize, serde::Deserialize, Clone)]
pub enum LamellarByteArray {
UnsafeArray(__UnsafeByteArray),
ReadOnlyArray(__ReadOnlyByteArray),
AtomicArray(__AtomicByteArray),
NativeAtomicArray(__NativeAtomicByteArray),
GenericAtomicArray(__GenericAtomicByteArray),
NetworkAtomicArray(__NetworkAtomicByteArray),
LocalLockArray(__LocalLockByteArray),
GlobalLockArray(__GlobalLockByteArray),
}
impl std::fmt::Debug for LamellarByteArray {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LamellarByteArray::UnsafeArray(_) => write!(f, "LamellarByteArray::UnsafeArray"),
LamellarByteArray::ReadOnlyArray(_) => write!(f, "LamellarByteArray::ReadOnlyArray"),
LamellarByteArray::AtomicArray(_) => write!(f, "LamellarByteArray::AtomicArray"),
LamellarByteArray::NativeAtomicArray(_) => {
write!(f, "LamellarByteArray::NativeAtomicArray")
}
LamellarByteArray::GenericAtomicArray(_) => {
write!(f, "LamellarByteArray::GenericAtomicArray")
}
LamellarByteArray::NetworkAtomicArray(_) => {
write!(f, "LamellarByteArray::NetworkAtomicArray")
}
LamellarByteArray::LocalLockArray(_) => write!(f, "LamellarByteArray::LocalLockArray"),
LamellarByteArray::GlobalLockArray(_) => {
write!(f, "LamellarByteArray::GlobalLockArray")
}
}
}
}
impl LamellarByteArray {
pub fn type_id(&self) -> std::any::TypeId {
match self {
LamellarByteArray::UnsafeArray(_) => std::any::TypeId::of::<__UnsafeByteArray>(),
LamellarByteArray::ReadOnlyArray(_) => std::any::TypeId::of::<__ReadOnlyByteArray>(),
LamellarByteArray::AtomicArray(_) => std::any::TypeId::of::<__AtomicByteArray>(),
LamellarByteArray::NativeAtomicArray(_) => {
std::any::TypeId::of::<__NativeAtomicByteArray>()
}
LamellarByteArray::GenericAtomicArray(_) => {
std::any::TypeId::of::<__GenericAtomicByteArray>()
}
LamellarByteArray::LocalLockArray(_) => std::any::TypeId::of::<__LocalLockByteArray>(),
LamellarByteArray::GlobalLockArray(_) => {
std::any::TypeId::of::<__GlobalLockByteArray>()
}
LamellarByteArray::NetworkAtomicArray(_) => {
std::any::TypeId::of::<__NetworkAtomicByteArray>()
}
}
}
pub fn num_elems_local(&self) -> usize {
match self {
LamellarByteArray::UnsafeArray(array) => array.inner.num_elems_local(),
LamellarByteArray::ReadOnlyArray(array) => array.array.inner.num_elems_local(),
LamellarByteArray::AtomicArray(array) => array.num_elems_local(),
LamellarByteArray::NativeAtomicArray(array) => array.array.inner.num_elems_local(),
LamellarByteArray::GenericAtomicArray(array) => array.array.inner.num_elems_local(),
LamellarByteArray::LocalLockArray(array) => array.array.inner.num_elems_local(),
LamellarByteArray::GlobalLockArray(array) => array.array.inner.num_elems_local(),
LamellarByteArray::NetworkAtomicArray(array) => array.array.inner.num_elems_local(),
}
}
pub(crate) fn team(&self) -> Darc<LamellarTeamRT> {
match self {
LamellarByteArray::UnsafeArray(array) => array.inner.data.inner().darc_rt_team(),
LamellarByteArray::ReadOnlyArray(array) => {
array.array.inner.data.inner().darc_rt_team()
}
LamellarByteArray::AtomicArray(array) => array.team(),
LamellarByteArray::NativeAtomicArray(array) => {
array.array.inner.data.inner().darc_rt_team()
}
LamellarByteArray::GenericAtomicArray(array) => {
array.array.inner.data.inner().darc_rt_team()
}
LamellarByteArray::LocalLockArray(array) => {
array.array.inner.data.inner().darc_rt_team()
}
LamellarByteArray::GlobalLockArray(array) => {
array.array.inner.data.inner().darc_rt_team()
}
LamellarByteArray::NetworkAtomicArray(array) => {
array.array.inner.data.inner().darc_rt_team()
}
}
}
pub(crate) fn spawn<F>(&self, f: F) -> LamellarTask<F::Output>
where
F: Future + Send + 'static,
F::Output: Send,
{
match self {
LamellarByteArray::UnsafeArray(array) => array.inner.spawn(f),
LamellarByteArray::ReadOnlyArray(array) => array.array.inner.spawn(f),
LamellarByteArray::AtomicArray(array) => array.spawn(f),
LamellarByteArray::NativeAtomicArray(array) => array.array.inner.spawn(f),
LamellarByteArray::GenericAtomicArray(array) => array.array.inner.spawn(f),
LamellarByteArray::LocalLockArray(array) => array.array.inner.spawn(f),
LamellarByteArray::GlobalLockArray(array) => array.array.inner.spawn(f),
LamellarByteArray::NetworkAtomicArray(array) => array.array.inner.spawn(f),
}
}
pub async fn local_data<'a, T: Dist>(&'a self) -> __LamellarLocalData<'a, T> {
match self {
LamellarByteArray::UnsafeArray(array) => __LamellarLocalData::Slice(array.local_data()),
LamellarByteArray::ReadOnlyArray(array) => {
__LamellarLocalData::Slice(array.local_data())
}
LamellarByteArray::AtomicArray(array) => match AtomicArray::from(array) {
AtomicArray::NativeAtomicArray(array) => {
__LamellarLocalData::NativeAtomic(array.local_data())
}
AtomicArray::GenericAtomicArray(array) => {
__LamellarLocalData::GenericAtomic(array.local_data())
}
AtomicArray::NetworkAtomicArray(array) => {
__LamellarLocalData::NetworkAtomic(array.local_data())
}
},
LamellarByteArray::NativeAtomicArray(array) => {
__LamellarLocalData::NativeAtomic(NativeAtomicArray::from(array).local_data())
}
LamellarByteArray::GenericAtomicArray(array) => {
__LamellarLocalData::GenericAtomic(GenericAtomicArray::from(array).local_data())
}
LamellarByteArray::LocalLockArray(array) => {
__LamellarLocalData::LocalLock(LocalLockArray::from(array).read_local_data().await)
}
LamellarByteArray::GlobalLockArray(array) => __LamellarLocalData::GlobalLock(
GlobalLockArray::from(array).read_local_data().await,
),
LamellarByteArray::NetworkAtomicArray(array) => {
__LamellarLocalData::NetworkAtomic(NetworkAtomicArray::from(array).local_data())
}
}
}
async fn mut_local_data<'a, T: Dist>(&'a mut self) -> __LamellarMutLocalData<'a, T> {
match self {
LamellarByteArray::UnsafeArray(ref mut array) => {
__LamellarMutLocalData::Slice(array.mut_local_data())
}
LamellarByteArray::ReadOnlyArray(ref mut _array) => {
panic!("ReadOnlyArray does not support mut_local_data")
}
LamellarByteArray::AtomicArray(ref mut array) => match AtomicArray::from(array) {
AtomicArray::NativeAtomicArray(ref mut array) => {
__LamellarMutLocalData::NativeAtomic(array.mut_local_data())
}
AtomicArray::GenericAtomicArray(ref mut array) => {
__LamellarMutLocalData::GenericAtomic(array.mut_local_data())
}
AtomicArray::NetworkAtomicArray(ref mut array) => {
__LamellarMutLocalData::NetworkAtomic(array.mut_local_data())
}
},
LamellarByteArray::NativeAtomicArray(ref mut array) => {
__LamellarMutLocalData::NativeAtomic(
NativeAtomicArray::from(array).mut_local_data(),
)
}
LamellarByteArray::GenericAtomicArray(ref mut array) => {
__LamellarMutLocalData::GenericAtomic(
GenericAtomicArray::from(array).mut_local_data(),
)
}
LamellarByteArray::LocalLockArray(ref mut array) => __LamellarMutLocalData::LocalLock(
LocalLockArray::from(array).write_local_data().await,
),
LamellarByteArray::GlobalLockArray(ref mut array) => {
__LamellarMutLocalData::GlobalLock(
GlobalLockArray::from(array).write_local_data().await,
)
}
LamellarByteArray::NetworkAtomicArray(ref mut array) => {
__LamellarMutLocalData::NetworkAtomic(
NetworkAtomicArray::from(array).mut_local_data(),
)
}
}
}
}
impl crate::active_messaging::DarcSerde for LamellarByteArray {
fn ser(&self, num_pes: usize, darcs: &mut Vec<RemotePtr>) {
match self {
LamellarByteArray::UnsafeArray(array) => array.ser(num_pes, darcs),
LamellarByteArray::ReadOnlyArray(array) => array.ser(num_pes, darcs),
LamellarByteArray::AtomicArray(array) => array.ser(num_pes, darcs),
LamellarByteArray::NativeAtomicArray(array) => array.ser(num_pes, darcs),
LamellarByteArray::GenericAtomicArray(array) => array.ser(num_pes, darcs),
LamellarByteArray::LocalLockArray(array) => array.ser(num_pes, darcs),
LamellarByteArray::GlobalLockArray(array) => array.ser(num_pes, darcs),
LamellarByteArray::NetworkAtomicArray(array) => array.ser(num_pes, darcs),
}
}
}
enum __LamellarMutLocalData<'a, T: Dist> {
Slice(&'a mut [T]),
LocalLock(LocalLockMutLocalData<T>),
GlobalLock(GlobalLockMutLocalData<T>),
NativeAtomic(__NativeAtomicLocalData<T>),
GenericAtomic(__GenericAtomicLocalData<T>),
NetworkAtomic(__NetworkAtomicLocalData<T>),
}
#[doc(hidden)]
pub enum __LamellarLocalData<'a, T: Dist> {
Slice(&'a [T]),
LocalLock(LocalLockLocalData<T>),
GlobalLock(GlobalLockLocalData<T>),
NativeAtomic(__NativeAtomicLocalData<T>),
GenericAtomic(__GenericAtomicLocalData<T>),
NetworkAtomic(__NetworkAtomicLocalData<T>),
}
impl<T: Dist> __LamellarLocalData<'_, T> {
pub fn reduce<Op>(self, reduce: Op) -> Option<T>
where
Op: Fn(T, T) -> T,
{
match self {
__LamellarLocalData::Slice(slice) => slice.iter().copied().reduce(reduce),
__LamellarLocalData::LocalLock(local_lock) => local_lock.iter().copied().reduce(reduce),
__LamellarLocalData::GlobalLock(global_lock) => {
global_lock.iter().copied().reduce(reduce)
}
__LamellarLocalData::NativeAtomic(native_atomic) => {
native_atomic.iter().map(|e| e.load()).reduce(reduce)
}
__LamellarLocalData::GenericAtomic(generic_atomic) => {
generic_atomic.iter().map(|e| e.load()).reduce(reduce)
}
__LamellarLocalData::NetworkAtomic(network_atomic) => {
network_atomic.iter().map(|e| e.load()).reduce(reduce)
}
}
}
}
impl<T: Dist + 'static> crate::active_messaging::DarcSerde for LamellarReadArray<T> {
fn ser(&self, num_pes: usize, darcs: &mut Vec<RemotePtr>) {
match self {
LamellarReadArray::UnsafeArray(array) => array.ser(num_pes, darcs),
LamellarReadArray::ReadOnlyArray(array) => array.ser(num_pes, darcs),
LamellarReadArray::AtomicArray(array) => array.ser(num_pes, darcs),
LamellarReadArray::LocalLockArray(array) => array.ser(num_pes, darcs),
LamellarReadArray::GlobalLockArray(array) => array.ser(num_pes, darcs),
}
}
}
impl<T: Dist> ActiveMessaging for LamellarReadArray<T> {
type SinglePeAmHandle<R: AmDist> = AmHandle<R>;
type MultiAmHandle<R: AmDist> = MultiAmHandle<R>;
type LocalAmHandle<L> = LocalAmHandle<L>;
fn exec_am_all<F>(&self, am: F) -> Self::MultiAmHandle<F::Output>
where
F: RemoteActiveMessage + LamellarAM + Serde + AmDist,
{
match self {
LamellarReadArray::UnsafeArray(array) => array.exec_am_all(am),
LamellarReadArray::ReadOnlyArray(array) => array.exec_am_all(am),
LamellarReadArray::AtomicArray(array) => array.exec_am_all(am),
LamellarReadArray::LocalLockArray(array) => array.exec_am_all(am),
LamellarReadArray::GlobalLockArray(array) => array.exec_am_all(am),
}
}
fn exec_am_pe<F>(&self, pe: usize, am: F) -> Self::SinglePeAmHandle<F::Output>
where
F: RemoteActiveMessage + LamellarAM + Serde + AmDist,
{
match self {
LamellarReadArray::UnsafeArray(array) => array.exec_am_pe(pe, am),
LamellarReadArray::ReadOnlyArray(array) => array.exec_am_pe(pe, am),
LamellarReadArray::AtomicArray(array) => array.exec_am_pe(pe, am),
LamellarReadArray::LocalLockArray(array) => array.exec_am_pe(pe, am),
LamellarReadArray::GlobalLockArray(array) => array.exec_am_pe(pe, am),
}
}
fn exec_am_local<F>(&self, am: F) -> Self::LocalAmHandle<F::Output>
where
F: LamellarActiveMessage + LocalAM + 'static,
{
match self {
LamellarReadArray::UnsafeArray(array) => array.exec_am_local(am),
LamellarReadArray::ReadOnlyArray(array) => array.exec_am_local(am),
LamellarReadArray::AtomicArray(array) => array.exec_am_local(am),
LamellarReadArray::LocalLockArray(array) => array.exec_am_local(am),
LamellarReadArray::GlobalLockArray(array) => array.exec_am_local(am),
}
}
fn wait_all(&self) {
match self {
LamellarReadArray::UnsafeArray(array) => array.wait_all(),
LamellarReadArray::ReadOnlyArray(array) => array.wait_all(),
LamellarReadArray::AtomicArray(array) => array.wait_all(),
LamellarReadArray::LocalLockArray(array) => array.wait_all(),
LamellarReadArray::GlobalLockArray(array) => array.wait_all(),
}
}
fn await_all(&self) -> impl Future<Output = ()> + Send {
let fut: Pin<Box<dyn Future<Output = ()> + Send>> = match self {
LamellarReadArray::UnsafeArray(array) => Box::pin(array.await_all()),
LamellarReadArray::ReadOnlyArray(array) => Box::pin(array.await_all()),
LamellarReadArray::AtomicArray(array) => Box::pin(array.await_all()),
LamellarReadArray::LocalLockArray(array) => Box::pin(array.await_all()),
LamellarReadArray::GlobalLockArray(array) => Box::pin(array.await_all()),
};
fut
}
fn barrier(&self) {
match self {
LamellarReadArray::UnsafeArray(array) => array.barrier(),
LamellarReadArray::ReadOnlyArray(array) => array.barrier(),
LamellarReadArray::AtomicArray(array) => array.barrier(),
LamellarReadArray::LocalLockArray(array) => array.barrier(),
LamellarReadArray::GlobalLockArray(array) => array.barrier(),
}
}
fn async_barrier(&self) -> BarrierHandle {
match self {
LamellarReadArray::UnsafeArray(array) => array.async_barrier(),
LamellarReadArray::ReadOnlyArray(array) => array.async_barrier(),
LamellarReadArray::AtomicArray(array) => array.async_barrier(),
LamellarReadArray::LocalLockArray(array) => array.async_barrier(),
LamellarReadArray::GlobalLockArray(array) => array.async_barrier(),
}
}
fn spawn<F>(&self, f: F) -> LamellarTask<F::Output>
where
F: Future + Send + 'static,
F::Output: Send,
{
match self {
LamellarReadArray::UnsafeArray(array) => array.spawn(f),
LamellarReadArray::ReadOnlyArray(array) => array.spawn(f),
LamellarReadArray::AtomicArray(array) => array.spawn(f),
LamellarReadArray::LocalLockArray(array) => array.spawn(f),
LamellarReadArray::GlobalLockArray(array) => array.spawn(f),
}
}
fn block_on<F: Future>(&self, f: F) -> F::Output {
match self {
LamellarReadArray::UnsafeArray(array) => array.block_on(f),
LamellarReadArray::ReadOnlyArray(array) => array.block_on(f),
LamellarReadArray::AtomicArray(array) => array.block_on(f),
LamellarReadArray::LocalLockArray(array) => array.block_on(f),
LamellarReadArray::GlobalLockArray(array) => array.block_on(f),
}
}
fn block_on_all<I>(&self, iter: I) -> Vec<<<I as IntoIterator>::Item as Future>::Output>
where
I: IntoIterator,
<I as IntoIterator>::Item: Future + Send + 'static,
<<I as IntoIterator>::Item as Future>::Output: Send,
{
match self {
LamellarReadArray::UnsafeArray(array) => array.block_on_all(iter),
LamellarReadArray::ReadOnlyArray(array) => array.block_on_all(iter),
LamellarReadArray::AtomicArray(array) => array.block_on_all(iter),
LamellarReadArray::LocalLockArray(array) => array.block_on_all(iter),
LamellarReadArray::GlobalLockArray(array) => array.block_on_all(iter),
}
}
}
impl<T: Dist> LamellarEnv for LamellarReadArray<T> {
fn my_pe(&self) -> usize {
match self {
LamellarReadArray::UnsafeArray(array) => array.my_pe(),
LamellarReadArray::ReadOnlyArray(array) => array.my_pe(),
LamellarReadArray::AtomicArray(array) => array.my_pe(),
LamellarReadArray::LocalLockArray(array) => array.my_pe(),
LamellarReadArray::GlobalLockArray(array) => array.my_pe(),
}
}
fn num_pes(&self) -> usize {
match self {
LamellarReadArray::UnsafeArray(array) => array.num_pes(),
LamellarReadArray::ReadOnlyArray(array) => array.num_pes(),
LamellarReadArray::AtomicArray(array) => array.num_pes(),
LamellarReadArray::LocalLockArray(array) => array.num_pes(),
LamellarReadArray::GlobalLockArray(array) => array.num_pes(),
}
}
fn num_threads_per_pe(&self) -> usize {
match self {
LamellarReadArray::UnsafeArray(array) => array.num_threads_per_pe(),
LamellarReadArray::ReadOnlyArray(array) => array.num_threads_per_pe(),
LamellarReadArray::AtomicArray(array) => array.num_threads_per_pe(),
LamellarReadArray::LocalLockArray(array) => array.num_threads_per_pe(),
LamellarReadArray::GlobalLockArray(array) => array.num_threads_per_pe(),
}
}
fn world(&self) -> Arc<LamellarTeam> {
match self {
LamellarReadArray::UnsafeArray(array) => array.world(),
LamellarReadArray::ReadOnlyArray(array) => array.world(),
LamellarReadArray::AtomicArray(array) => array.world(),
LamellarReadArray::LocalLockArray(array) => array.world(),
LamellarReadArray::GlobalLockArray(array) => array.world(),
}
}
fn team(&self) -> Arc<LamellarTeam> {
match self {
LamellarReadArray::UnsafeArray(array) => array.team(),
LamellarReadArray::ReadOnlyArray(array) => array.team(),
LamellarReadArray::AtomicArray(array) => array.team(),
LamellarReadArray::LocalLockArray(array) => array.team(),
LamellarReadArray::GlobalLockArray(array) => array.team(),
}
}
}
#[enum_dispatch]
#[derive(serde::Serialize, serde::Deserialize, Clone)]
#[serde(bound = "T: Dist + serde::Serialize + serde::de::DeserializeOwned")]
pub enum LamellarWriteArray<T: Dist> {
UnsafeArray(UnsafeArray<T>),
AtomicArray(AtomicArray<T>),
LocalLockArray(LocalLockArray<T>),
GlobalLockArray(GlobalLockArray<T>),
}
impl<T: Dist + 'static> crate::active_messaging::DarcSerde for LamellarWriteArray<T> {
fn ser(&self, num_pes: usize, darcs: &mut Vec<RemotePtr>) {
match self {
LamellarWriteArray::UnsafeArray(array) => array.ser(num_pes, darcs),
LamellarWriteArray::AtomicArray(array) => array.ser(num_pes, darcs),
LamellarWriteArray::LocalLockArray(array) => array.ser(num_pes, darcs),
LamellarWriteArray::GlobalLockArray(array) => array.ser(num_pes, darcs),
}
}
}
impl<T: Dist> ActiveMessaging for LamellarWriteArray<T> {
type SinglePeAmHandle<R: AmDist> = AmHandle<R>;
type MultiAmHandle<R: AmDist> = MultiAmHandle<R>;
type LocalAmHandle<L> = LocalAmHandle<L>;
fn exec_am_all<F>(&self, am: F) -> Self::MultiAmHandle<F::Output>
where
F: RemoteActiveMessage + LamellarAM + Serde + AmDist,
{
match self {
LamellarWriteArray::UnsafeArray(array) => array.exec_am_all(am),
LamellarWriteArray::AtomicArray(array) => array.exec_am_all(am),
LamellarWriteArray::LocalLockArray(array) => array.exec_am_all(am),
LamellarWriteArray::GlobalLockArray(array) => array.exec_am_all(am),
}
}
fn exec_am_pe<F>(&self, pe: usize, am: F) -> Self::SinglePeAmHandle<F::Output>
where
F: RemoteActiveMessage + LamellarAM + Serde + AmDist,
{
match self {
LamellarWriteArray::UnsafeArray(array) => array.exec_am_pe(pe, am),
LamellarWriteArray::AtomicArray(array) => array.exec_am_pe(pe, am),
LamellarWriteArray::LocalLockArray(array) => array.exec_am_pe(pe, am),
LamellarWriteArray::GlobalLockArray(array) => array.exec_am_pe(pe, am),
}
}
fn exec_am_local<F>(&self, am: F) -> Self::LocalAmHandle<F::Output>
where
F: LamellarActiveMessage + LocalAM + 'static,
{
match self {
LamellarWriteArray::UnsafeArray(array) => array.exec_am_local(am),
LamellarWriteArray::AtomicArray(array) => array.exec_am_local(am),
LamellarWriteArray::LocalLockArray(array) => array.exec_am_local(am),
LamellarWriteArray::GlobalLockArray(array) => array.exec_am_local(am),
}
}
fn wait_all(&self) {
match self {
LamellarWriteArray::UnsafeArray(array) => array.wait_all(),
LamellarWriteArray::AtomicArray(array) => array.wait_all(),
LamellarWriteArray::LocalLockArray(array) => array.wait_all(),
LamellarWriteArray::GlobalLockArray(array) => array.wait_all(),
}
}
fn await_all(&self) -> impl Future<Output = ()> + Send {
let fut: Pin<Box<dyn Future<Output = ()> + Send>> = match self {
LamellarWriteArray::UnsafeArray(array) => Box::pin(array.await_all()),
LamellarWriteArray::AtomicArray(array) => Box::pin(array.await_all()),
LamellarWriteArray::LocalLockArray(array) => Box::pin(array.await_all()),
LamellarWriteArray::GlobalLockArray(array) => Box::pin(array.await_all()),
};
fut
}
fn barrier(&self) {
match self {
LamellarWriteArray::UnsafeArray(array) => array.barrier(),
LamellarWriteArray::AtomicArray(array) => array.barrier(),
LamellarWriteArray::LocalLockArray(array) => array.barrier(),
LamellarWriteArray::GlobalLockArray(array) => array.barrier(),
}
}
fn async_barrier(&self) -> BarrierHandle {
match self {
LamellarWriteArray::UnsafeArray(array) => array.async_barrier(),
LamellarWriteArray::AtomicArray(array) => array.async_barrier(),
LamellarWriteArray::LocalLockArray(array) => array.async_barrier(),
LamellarWriteArray::GlobalLockArray(array) => array.async_barrier(),
}
}
fn spawn<F>(&self, f: F) -> LamellarTask<F::Output>
where
F: Future + Send + 'static,
F::Output: Send,
{
match self {
LamellarWriteArray::UnsafeArray(array) => array.spawn(f),
LamellarWriteArray::AtomicArray(array) => array.spawn(f),
LamellarWriteArray::LocalLockArray(array) => array.spawn(f),
LamellarWriteArray::GlobalLockArray(array) => array.spawn(f),
}
}
fn block_on<F: Future>(&self, f: F) -> F::Output {
match self {
LamellarWriteArray::UnsafeArray(array) => array.block_on(f),
LamellarWriteArray::AtomicArray(array) => array.block_on(f),
LamellarWriteArray::LocalLockArray(array) => array.block_on(f),
LamellarWriteArray::GlobalLockArray(array) => array.block_on(f),
}
}
fn block_on_all<I>(&self, iter: I) -> Vec<<<I as IntoIterator>::Item as Future>::Output>
where
I: IntoIterator,
<I as IntoIterator>::Item: Future + Send + 'static,
<<I as IntoIterator>::Item as Future>::Output: Send,
{
match self {
LamellarWriteArray::UnsafeArray(array) => array.block_on_all(iter),
LamellarWriteArray::AtomicArray(array) => array.block_on_all(iter),
LamellarWriteArray::LocalLockArray(array) => array.block_on_all(iter),
LamellarWriteArray::GlobalLockArray(array) => array.block_on_all(iter),
}
}
}
impl<T: Dist> LamellarEnv for LamellarWriteArray<T> {
fn my_pe(&self) -> usize {
match self {
LamellarWriteArray::UnsafeArray(array) => array.my_pe(),
LamellarWriteArray::AtomicArray(array) => array.my_pe(),
LamellarWriteArray::LocalLockArray(array) => array.my_pe(),
LamellarWriteArray::GlobalLockArray(array) => array.my_pe(),
}
}
fn num_pes(&self) -> usize {
match self {
LamellarWriteArray::UnsafeArray(array) => array.num_pes(),
LamellarWriteArray::AtomicArray(array) => array.num_pes(),
LamellarWriteArray::LocalLockArray(array) => array.num_pes(),
LamellarWriteArray::GlobalLockArray(array) => array.num_pes(),
}
}
fn num_threads_per_pe(&self) -> usize {
match self {
LamellarWriteArray::UnsafeArray(array) => array.num_threads_per_pe(),
LamellarWriteArray::AtomicArray(array) => array.num_threads_per_pe(),
LamellarWriteArray::LocalLockArray(array) => array.num_threads_per_pe(),
LamellarWriteArray::GlobalLockArray(array) => array.num_threads_per_pe(),
}
}
fn world(&self) -> Arc<LamellarTeam> {
match self {
LamellarWriteArray::UnsafeArray(array) => array.world(),
LamellarWriteArray::AtomicArray(array) => array.world(),
LamellarWriteArray::LocalLockArray(array) => array.world(),
LamellarWriteArray::GlobalLockArray(array) => array.world(),
}
}
fn team(&self) -> Arc<LamellarTeam> {
match self {
LamellarWriteArray::UnsafeArray(array) => array.team(),
LamellarWriteArray::AtomicArray(array) => array.team(),
LamellarWriteArray::LocalLockArray(array) => array.team(),
LamellarWriteArray::GlobalLockArray(array) => array.team(),
}
}
}
#[doc(hidden)]
pub trait InnerArray: Sized {
fn as_inner(&self) -> &r#unsafe::private::UnsafeArrayInner;
}
pub(crate) mod private {
use crate::array::{
rdma::private::LamellarRdmaGet, AtomicArray, GenericAtomicArray, LamellarByteArray,
LamellarReadArray, LamellarWriteArray, NativeAtomicArray, NetworkAtomicArray, UnsafeArray,
};
use crate::memregion::Dist;
use crate::LamellarTeamRT;
use crate::{active_messaging::*, Darc};
use enum_dispatch::enum_dispatch;
use std::sync::Arc;
#[enum_dispatch(LamellarReadArray<T>,LamellarWriteArray<T>)]
pub trait LamellarArrayPrivate<T: Dist>: Clone + LamellarRdmaGet<T> {
fn inner_array(&self) -> &UnsafeArray<T>;
fn local_as_ptr(&self) -> *const T;
fn local_as_mut_ptr(&self) -> *mut T;
fn pe_for_dist_index(&self, index: usize) -> Option<usize>;
fn pe_offset_for_dist_index(&self, pe: usize, index: usize) -> Option<usize>;
unsafe fn into_inner(self) -> UnsafeArray<T>;
fn as_lamellar_byte_array(&self) -> LamellarByteArray;
}
#[enum_dispatch(LamellarReadArray<T>,LamellarWriteArray<T>)]
pub(crate) trait ArrayExecAm<T: Dist> {
fn team_rt(&self) -> Darc<LamellarTeamRT>;
fn team_counters(&self) -> Arc<AMCounters>;
fn exec_am_local_tg<F>(&self, am: F) -> LocalAmHandle<F::Output>
where
F: LamellarActiveMessage + LocalAM + 'static,
{
self.team_rt()
.exec_am_local_tg(am, Some(self.team_counters()), None)
}
fn spawn_am_local_tg<F>(&self, am: F) -> LocalAmHandle<F::Output>
where
F: LamellarActiveMessage + LocalAM + 'static,
{
self.team_rt()
.spawn_am_local_tg(am, Some(self.team_counters()), None)
}
fn exec_am_pe_tg<F>(&self, pe: usize, am: F) -> AmHandle<F::Output>
where
F: RemoteActiveMessage + LamellarAM + AmDist,
{
self.team_rt()
.exec_am_pe_tg(pe, am, Some(self.team_counters()))
}
fn spawn_am_pe_tg<F>(&self, pe: usize, am: F) -> AmHandle<F::Output>
where
F: RemoteActiveMessage + LamellarAM + AmDist,
{
self.team_rt()
.spawn_am_pe_tg(pe, am, Some(self.team_counters()))
}
fn exec_am_all_tg<F>(&self, am: F) -> MultiAmHandle<F::Output>
where
F: RemoteActiveMessage + LamellarAM + AmDist,
{
self.team_rt()
.exec_am_all_tg(am, Some(self.team_counters()))
}
fn spawn_am_all_tg<F>(&self, am: F) -> MultiAmHandle<F::Output>
where
F: RemoteActiveMessage + LamellarAM + AmDist,
{
self.team_rt()
.spawn_am_all_tg(am, Some(self.team_counters()))
}
}
}
#[enum_dispatch(LamellarReadArray<T>,LamellarWriteArray<T>)]
pub trait LamellarArray<T: Dist>:
private::LamellarArrayPrivate<T> + ActiveMessaging + LamellarEnv
{
#[doc(alias("One-sided", "onesided"))]
fn len(&self) -> usize;
#[doc(alias("One-sided", "onesided"))]
fn num_elems_local(&self) -> usize;
#[doc(alias("One-sided", "onesided"))]
fn pe_and_offset_for_global_index(&self, index: usize) -> Option<(usize, usize)>;
#[doc(alias("One-sided", "onesided"))]
fn first_global_index_for_pe(&self, pe: usize) -> Option<usize>;
#[doc(alias("One-sided", "onesided"))]
fn last_global_index_for_pe(&self, pe: usize) -> Option<usize>;
}
pub trait SubArray<T: Dist>: LamellarArray<T> {
#[doc(hidden)]
type Array: LamellarArray<T>;
#[doc(alias("One-sided", "onesided"))]
fn sub_array<R: std::ops::RangeBounds<usize>>(&self, range: R) -> Self::Array;
#[doc(alias("One-sided", "onesided"))]
fn global_index(&self, sub_index: usize) -> usize;
}
pub trait ArrayPrint<T: Dist + std::fmt::Debug>: LamellarArray<T> {
#[doc(alias = "Collective")]
fn print(&self);
}
pub trait LamellarArrayReduce<T>
where
T: Dist + AmDist + 'static,
{
type Handle;
#[doc(alias("One-sided", "onesided"))]
fn registered_reduce(&self, reduction: &str) -> Self::Handle;
}
pub use lamellar_impl::register_reduction;