ruPRIM 0.1.6

Parallel primitives, reductions, scans, and indexing for Ruda.
Documentation
use ruda_core::ir::features::AtomicUsage;
use ruda_kernel::dsl as kernel_dsl;
use ruda_kernel::dsl::{Runtime, calculate_ruda_count_elemwise, prelude::*};
use ruda_kernel::library::{FastDivmod, tensor::layout::linear::LinearView};
use ruda_kernel::tensor::{RudaTensor, layout::{address_type, shape_divmod_range}};

pub(super) fn scatter_nd_add<R: Runtime>(
    tensor: RudaTensor<R>,
    indices: RudaTensor<R>,
    values: RudaTensor<R>,
) -> RudaTensor<R> {
    let idx_shape = indices.meta.shape();
    let m = idx_shape.len();
    let k = idx_shape[m - 1];
    let num_updates: usize = idx_shape[..m - 1].iter().product();
    let slice_size: usize = tensor.meta.shape()[k..].iter().product();
    let total = num_updates * slice_size;
    let supports_atomic_add = tensor.client.properties()
        .atomic_type_usage(Type::new(StorageType::Atomic(tensor.dtype.into())))
        .contains(AtomicUsage::Add);
    let working_units = if supports_atomic_add { total } else { slice_size };
    let ruda_dim = RudaDim::new(tensor.client.properties(), working_units);
    let ruda_count = calculate_ruda_count_elemwise(&tensor.client, working_units, ruda_dim);
    let launch = if supports_atomic_add {
        atomic_add_kernel::launch_unchecked::<R>
    } else {
        ordered_add_kernel::launch_unchecked::<R>
    };
    let dtypes = [tensor.dtype.into(), indices.dtype.into()];
    let data_slice_shape = shape_divmod_range(&tensor, k..tensor.meta.num_dims());
    let address_type = address_type!(tensor, indices, values).max(AddressType::from_len(
        total.max(indices.meta.num_elements()),
    ));

    unsafe {
        launch(
            &tensor.client,
            ruda_count,
            ruda_dim,
            address_type,
            tensor.clone().into_tensor_arg(),
            indices.into_linear_view(),
            values.into_linear_view(),
            data_slice_shape,
            slice_size,
            k,
            num_updates,
            dtypes,
        );
    }
    tensor
}

#[ruda(launch_unchecked, address_type = "dynamic")]
fn atomic_add_kernel<T: Numeric, I: Int>(
    data: &mut Tensor<Atomic<T>>,
    indices: &LinearView<I>,
    values: &LinearView<T>,
    data_slice_shape: Sequence<FastDivmod<usize>>,
    slice_size: usize,
    k: usize,
    num_updates: usize,
    #[define(T, I)] _dtypes: [StorageType; 2],
) {
    if ABSOLUTE_POS >= num_updates * slice_size {
        terminate!();
    }
    let update_idx = ABSOLUTE_POS / slice_size;
    let mut remainder = ABSOLUTE_POS % slice_size;
    let slice_rank = data_slice_shape.len().comptime();
    let mut data_idx = 0usize;
    #[unroll]
    for i in 0..slice_rank {
        let dim = slice_rank - i - 1;
        let (rem, coord) = data_slice_shape[dim].div_mod(remainder);
        remainder = rem;
        data_idx += coord * data.stride(k + dim);
    }
    for j in 0..k {
        data_idx += usize::cast_from(indices[update_idx * k + j]) * data.stride(j);
    }
    data[data_idx].fetch_add(values[ABSOLUTE_POS]);
}

#[ruda(launch_unchecked, address_type = "dynamic")]
fn ordered_add_kernel<T: Numeric, I: Int>(
    data: &mut Tensor<T>,
    indices: &LinearView<I>,
    values: &LinearView<T>,
    data_slice_shape: Sequence<FastDivmod<usize>>,
    slice_size: usize,
    k: usize,
    num_updates: usize,
    #[define(T, I)] _dtypes: [StorageType; 2],
) {
    if ABSOLUTE_POS >= slice_size {
        terminate!();
    }
    let mut remainder = ABSOLUTE_POS;
    let slice_rank = data_slice_shape.len().comptime();
    let mut slice_offset = 0usize;
    #[unroll]
    for i in 0..slice_rank {
        let dim = slice_rank - i - 1;
        let (rem, coord) = data_slice_shape[dim].div_mod(remainder);
        remainder = rem;
        slice_offset += coord * data.stride(k + dim);
    }
    for update_idx in 0..num_updates {
        let mut data_idx = slice_offset;
        for j in 0..k {
            data_idx += usize::cast_from(indices[update_idx * k + j]) * data.stride(j);
        }
        data[data_idx] = data[data_idx] + values[update_idx * slice_size + ABSOLUTE_POS];
    }
}