ruPRIM
English | 简体中文 | 日本語 | Deutsch | Русский
Parallel primitives, reductions, scans, and indexing for Ruda.
- Cargo package:
ruPRIM - Rust crate:
ruprim
Features
| Feature | Operations |
|---|---|
tensor-reduce |
Whole-tensor and axis reductions |
tensor-reduce-autotune |
Reduction autotuning |
tensor-scan |
Cumulative sum, product, minimum, and maximum |
elementwise |
Elementwise operations |
indexing |
Selection, slicing, gather, and scatter |
Quick Start
Build from the RUDA workspace:
Documentation
ruPRIM User Guide
Compute libraries · Tensor framework · 中文
ruPRIM provides device tensor reductions, cumulative scans, elementwise operations, and indexing. This page uses RudaTensor<R>, where R is a device Runtime.
1. Configure dependencies
The Cargo package is ruPRIM; its Rust import name is ruprim. Enable features for the operations you need:
| Feature | Interface |
|---|---|
tensor-reduce |
ruprim::reduce::tensor: whole-tensor and axis reductions |
tensor-reduce-autotune |
Reduction autotuning; also enables tensor-reduce |
tensor-scan |
ruprim::scan: cumulative sum, product, minimum, maximum |
elementwise |
ruprim::elementwise: elementwise computation |
indexing |
ruprim::indexing: selection, slicing, gather, scatter; also enables elementwise |
This configuration places the application directory alongside the RUDA source directory. See Getting started for NVIDIA setup.
[]
= { = "ruPRIM", = "../RUDA/ruPRIM", = false, = ["std", "tensor-reduce", "tensor-scan", "indexing"] }
= { = "../RUDA/ruda-core", = false, = ["std", "tensor-host-data"] }
= { = "../RUDA/ruda-kernel", = false, = ["frontend-std", "device-tensor"] }
= { = "../RUDA/ruda-driver-cuda", = false, = ["std"] }
2. Sum, row reductions, and scans
This complete src/main.rs uses F32 matrix [[1, 2, 3], [4, 5, 6]] to compute its total, row means, row argmax indices, and row prefix sums. Run cargo run from the application directory:
use ;
use ;
use ;
use ;
use cumsum;
sum reduces all elements and returns shape [1]. reduce_dim handles only the selected axis, normally preserving rank and setting that axis length to 1. Row means therefore have shape [2, 1], not [2].
3. Reduction operations and strategies
Full parameter order for tensor interfaces:
| Function | Purpose |
|---|---|
sum(tensor, strategy) |
Whole-tensor sum |
sum_fallback(tensor, strategy) |
Whole-tensor sum; switches OneShot to Chained when the required atomic add is unavailable |
reduce(tensor, output_dtype, strategy, config) |
Reduces every axis in turn and returns shape [1] |
reduce_dim(tensor, output_dtype, dim, strategy, config) |
Reduces a selected axis |
Select config with ReduceOperationConfig:
| Operation | reduce_dim output |
|---|---|
Sum, Prod, Mean |
Sum, product, mean; selected axis length 1 |
Min, Max, MaxAbs |
Minimum, maximum, maximum absolute value; selected axis length 1 |
ArgMin, ArgMax |
Zero-based indices within the selected axis; axis length 1 |
TopK(k) |
Top k values along the selected axis; axis length k |
ArgTopK(k) |
Corresponding indices within the axis; axis length k |
Index reductions require an explicit integer output dtype such as Some(DType::I32). For value reductions, pass None; output retains input dtype. Do not use output_dtype as a general cast parameter. F16/BF16 Sum, Prod, and Mean use FP32 accumulation in this path before writing the input dtype. The axis must be valid, and TopK k should be in 1..=axis length. Use reduce_dim for TopK, not whole-tensor reduce, which resets the final shape to [1].
Choose SumStrategy as follows:
OneShot(ruda_count): explicitly sets a positive workgroup count and requires atomic add for the input dtype.Chained(KernelReduceStrategy::Unspecified): uses staged reductions without requiring the whole-tensor sum's global atomic add; used in the example.Autotune: available withtensor-reduce-autotune. Without that feature the default isOneShot(4); with it the default isAutotune.
KernelReduceStrategy offers Unspecified, Specific(ReduceStrategy), and feature-gated Autotune. Use Specific to fix a low-level strategy. Without autotuning, the default is Unspecified.
Reduction interfaces return Result<RudaTensor<R>, ReduceError>. An out-of-range axis returns InvalidAxis; missing atomic add for OneShot returns MissingAtomicAdd. sum_fallback replaces only the unsupported OneShot atomic-add case; it does not switch to CPU execution.
4. Cumulative scans
All four functions take (tensor, dim):
| Function | Result for one input row [3, 1, 2] |
|---|---|
cumsum |
[3, 4, 6] |
cumprod |
[3, 3, 6] |
cummin |
[3, 1, 1] |
cummax |
[3, 3, 3] |
These are inclusive prefix operations. Output shape and dtype match the input; batch rows are processed independently. dim is zero-based and must be within the input rank. Scans return tensors directly, not Result.
The current algorithm reads the corresponding prefix for each output position, giving O(n²) total reads for an axis of length n. Account for this cost on long scan axes rather than estimating work from input element count alone.
5. Selection, gather, slicing, and integer powers
Add this function to the same file and call indexing_example(&device)?; before main returns. It reuses the earlier imports:
| Function | Indices and output |
|---|---|
select(tensor, dim, indices) |
One-dimensional integer indices; applies the same positions across other dimensions, replacing selected-axis length with index count |
gather(dim, tensor, indices) |
dim is the first argument; chooses an input value per output position, with output shape equal to indices shape |
slice(tensor, ranges) |
Half-open start..end ranges per axis; the example selects columns 1 and 2 from all rows |
slice_assign(tensor, slices, value) |
Uses ruda_core::tensor::Slice; writes value into the selected region and returns the updated tensor |
Indices and data must share a device; indices must be valid zero-based integers. For gather, use index tensors with the same rank and matching non-selected dimensions. Each example row can select different columns. Slice ranges must lie within their axes, and assignment value shape must match the selected region.
Integer powers are in ruprim::elementwise::binary::integer_power:
scalar(input, Scalar::Int(exponent)): uses one integer exponent for all elements.tensor(base, exponents): uses floating-point bases and integer exponent tensors with broadcast-compatible shapes; output retains base dtype.
Use Scalar::Int(-2) for a negative exponent rather than converting the exponent to floating point. Indexing and elementwise interfaces return device tensors directly. To retain an input for other operations, pass cloned handles as in the example and keep the tensor returned by each operation.
API reference: Reductions, Scans, Indexing, Integer powers.