ruPRIM 0.1.9

Parallel primitives, reductions, scans, and indexing for Ruda.
Documentation
use super::{allocate_output, packing_layout, raw_dtype, read_value};
use ruda_core::tensor::{QTensorPrimitive, QuantScheme, TensorMetadata};
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}};

pub fn quantized_gather<R: Runtime>(
    dim: usize,
    tensor: RudaTensor<R>,
    indices: RudaTensor<R>,
) -> RudaTensor<R> {
    assert_eq!(tensor.rank(), indices.rank(), "Gather rank mismatch");
    assert!(dim < tensor.rank(), "Gather dimension out of bounds");
    for axis in 0..tensor.rank() {
        if axis != dim {
            assert_eq!(tensor.meta.shape()[axis], indices.meta.shape()[axis], "Gather shape mismatch");
        }
    }
    let output = allocate_output(&tensor, indices.shape());
    let (values, _) = tensor.quantized_handles().unwrap();
    let (out_values, _) = output.quantized_handles().unwrap();
    let num_elems = out_values.meta.num_elements();
    if num_elems == 0 {
        return output;
    }

    let (axis, inner, axis_len) = packing_layout(&output);
    let ruda_dim = RudaDim::new(output.client.properties(), num_elems);
    let ruda_count = calculate_ruda_count_elemwise(&output.client, num_elems, ruda_dim);
    let dtypes = [raw_dtype(values.dtype).into(), indices.dtype.into()];
    unsafe {
        gather_kernel::launch_unchecked(
            &output.client,
            ruda_count,
            ruda_dim,
            address_type!(values, indices, out_values).max(AddressType::from_len(
                tensor.meta.num_elements().max(output.meta.num_elements()),
            )),
            values.into_tensor_arg(),
            indices.into_linear_view(),
            out_values.clone().into_linear_view(),
            shape_divmod(&out_values),
            inner,
            axis_len,
            axis,
            dim,
            *tensor.scheme(),
            dtypes,
        );
    }
    output
}

#[ruda(launch_unchecked, address_type = "dynamic")]
fn gather_kernel<T: Int, I: Numeric>(
    input: &Tensor<T>,
    indices: &LinearView<I>,
    output: &mut LinearView<T, ReadWrite>,
    out_shape: Sequence<FastDivmod<usize>>,
    inner: usize,
    axis_len: usize,
    #[comptime] packed_axis: usize,
    #[comptime] selected_axis: usize,
    #[comptime] scheme: QuantScheme,
    #[define(T, I)] _dtypes: [StorageType; 2],
) {
    if !output.is_in_bounds(ABSOLUTE_POS) {
        terminate!();
    }
    let rank = out_shape.len().comptime();
    let packing = scheme.num_quants();
    let bits = scheme.value.size_bits();
    let axis_words = axis_len / packing + usize::cast_from(axis_len % packing != 0);
    let group = ABSOLUTE_POS / inner;
    let word = group % axis_words;
    let outer = group / axis_words;
    let inner_pos = ABSOLUTE_POS % inner;
    let base = (outer * axis_len + word * packing) * inner + inner_pos;
    let mut packed = T::new(0);
    #[unroll]
    for lane in 0..packing {
        if word * packing + lane < axis_len {
            let selected = usize::cast_from(indices[base + lane * inner]);
            let mut remainder = ABSOLUTE_POS;
            let mut offset = 0;
            let mut slot = 0;
            #[unroll]
            for i in 0..rank {
                let axis = rank - i - 1;
                let (rem, mut coord) = out_shape[axis].div_mod(remainder);
                remainder = rem;
                if axis == packed_axis {
                    coord = coord * packing + lane;
                }
                if axis == selected_axis {
                    coord = selected;
                }
                if axis == packed_axis {
                    slot = coord % packing;
                    coord /= packing;
                }
                offset += coord * input.stride(axis);
            }
            let value = read_value(input, offset, slot, scheme);
            packed |= value << T::cast_from(lane * bits);
        }
    }
    output[ABSOLUTE_POS] = packed;
}