#![allow(clippy::too_many_arguments)]
use cuda_device::atomic::{AtomicOrdering, DeviceAtomicU64};
use cuda_device::{cuda_module, kernel, launch_bounds, launch_contract, thread, warp};
use cuda_device::{DisjointSlice, Runtime2DIndex, SharedArray};
use crate::matrix::MAX_SMALL_COLS;
use crate::ScalarCuda;
const MAX_SMALL_COLS_SQ: usize = MAX_SMALL_COLS * MAX_SMALL_COLS;
pub(crate) const BLOCK_SIZE: u32 = 256;
pub(crate) const WARPS_PER_BLOCK: usize = BLOCK_SIZE as usize / 32;
pub(crate) const SMALL_NSTATES: u32 = 85;
pub(crate) const REDUCE_ELEM_SMALL_NSTATES: u32 = 16;
pub(crate) const REDUCE_BATCH_SMALL_NSTATES: u32 = 12_288;
#[inline(always)]
fn split(i: usize, nstates: u32) -> (usize, usize) {
let nstates = nstates as usize;
(i / nstates, i % nstates)
}
#[inline(always)]
fn broadcast_src(b: usize, src_stride: u32, src_nbatch: u32, nbatch: u32, elem: usize) -> usize {
let src_b = b * src_nbatch as usize / nbatch as usize;
src_b * src_stride as usize + elem
}
#[inline(always)]
fn grid_stride() -> usize {
(thread::blockDim_x() * thread::gridDim_x()) as usize
}
#[inline(always)]
fn block_slot(b: usize) -> usize {
b * thread::gridDim_x() as usize + thread::blockIdx_x() as usize
}
#[inline(always)]
fn lane_block(nstates: u32, cols_per_block: u32) -> (usize, usize, usize) {
let cols = cols_per_block as usize;
(thread::blockIdx_x() as usize * cols, nstates as usize, cols)
}
#[inline(always)]
fn lane_element(
first: usize,
nstates: usize,
cols: usize,
nbatch: u32,
tid: usize,
) -> Option<(usize, usize)> {
if tid >= cols * nstates {
return None;
}
let b = first + tid / nstates;
if b >= nbatch as usize {
return None;
}
Some((b, tid % nstates))
}
#[inline(always)]
fn lane_loop(blocks_per_lane: u32) -> (usize, usize, usize, usize) {
let bpl = blocks_per_lane as usize;
let block = thread::blockIdx_x() as usize;
(
block / bpl,
block % bpl,
thread::gridDim_x() as usize / bpl,
bpl * thread::blockDim_x() as usize,
)
}
#[inline(always)]
fn lane_start(blk: usize) -> usize {
blk * thread::blockDim_x() as usize + thread::threadIdx_x() as usize
}
#[derive(Clone, Copy)]
pub struct LaneArgs<T, const K: usize> {
pub ptr: [*const T; K],
pub nstates: [u32; K],
pub nbatch: [u32; K],
}
#[derive(Clone, Copy)]
pub struct LaneArgsMut<T, const K: usize> {
pub ptr: [*mut T; K],
pub nstates: [u32; K],
}
#[cuda_module]
pub mod kernels {
use super::*;
const RELAXED: AtomicOrdering = AtomicOrdering::Relaxed;
#[kernel]
pub fn bundle_anchor() {}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (lhs.len() == n))]
pub fn vec_fill<T: ScalarCuda>(mut lhs: DisjointSlice<T>, value: T, n: u32) {
let idx = thread::index_1d();
if idx.get() < n as usize {
if let Some(elem) = lhs.get_mut(idx) {
*elem = value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (lhs.len() == n))]
pub fn vec_sub_assign_rev<T: ScalarCuda>(
mut lhs: DisjointSlice<T>,
rhs: &[T],
n: u32,
nstates: u32,
rhs_stride: u32,
rhs_nbatch: u32,
nbatch: u32,
) {
let idx = thread::index_1d();
let i = idx.get();
if i < n as usize {
let (b, elem) = split(i, nstates);
let value = rhs[broadcast_src(b, rhs_stride, rhs_nbatch, nbatch, elem)];
if let Some(elem) = lhs.get_mut(idx) {
*elem = value - *elem;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (lhs.len() == n))]
pub fn vec_mul_assign<T: ScalarCuda>(
mut lhs: DisjointSlice<T>,
rhs: &[T],
n: u32,
nstates: u32,
rhs_stride: u32,
rhs_nbatch: u32,
nbatch: u32,
) {
let idx = thread::index_1d();
let i = idx.get();
if i < n as usize {
let (b, elem) = split(i, nstates);
let value = rhs[broadcast_src(b, rhs_stride, rhs_nbatch, nbatch, elem)];
if let Some(elem) = lhs.get_mut(idx) {
*elem *= value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (lhs.len() == n))]
pub fn vec_div_assign<T: ScalarCuda>(
mut lhs: DisjointSlice<T>,
rhs: &[T],
n: u32,
nstates: u32,
rhs_stride: u32,
rhs_nbatch: u32,
nbatch: u32,
) {
let idx = thread::index_1d();
let i = idx.get();
if i < n as usize {
let (b, elem) = split(i, nstates);
let value = rhs[broadcast_src(b, rhs_stride, rhs_nbatch, nbatch, elem)];
if let Some(elem) = lhs.get_mut(idx) {
*elem /= value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (ret.len() == n))]
pub fn vec_add<T: ScalarCuda>(
mut ret: DisjointSlice<T>,
lhs: &[T],
rhs: &[T],
n: u32,
nstates: u32,
lhs_stride: u32,
lhs_nbatch: u32,
rhs_stride: u32,
rhs_nbatch: u32,
nbatch: u32,
) {
let idx = thread::index_1d();
let i = idx.get();
if i < n as usize {
let (b, elem) = split(i, nstates);
let a = lhs[broadcast_src(b, lhs_stride, lhs_nbatch, nbatch, elem)];
let c = rhs[broadcast_src(b, rhs_stride, rhs_nbatch, nbatch, elem)];
if let Some(elem) = ret.get_mut(idx) {
*elem = a + c;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (ret.len() == n))]
pub fn vec_sub<T: ScalarCuda>(
mut ret: DisjointSlice<T>,
lhs: &[T],
rhs: &[T],
n: u32,
nstates: u32,
lhs_stride: u32,
lhs_nbatch: u32,
rhs_stride: u32,
rhs_nbatch: u32,
nbatch: u32,
) {
let idx = thread::index_1d();
let i = idx.get();
if i < n as usize {
let (b, elem) = split(i, nstates);
let a = lhs[broadcast_src(b, lhs_stride, lhs_nbatch, nbatch, elem)];
let c = rhs[broadcast_src(b, rhs_stride, rhs_nbatch, nbatch, elem)];
if let Some(elem) = ret.get_mut(idx) {
*elem = a - c;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (ret.len() == n))]
pub fn vec_mul_scalar<T: ScalarCuda>(
mut ret: DisjointSlice<T>,
src: &[T],
scalar: T,
n: u32,
nstates: u32,
src_stride: u32,
src_nbatch: u32,
nbatch: u32,
) {
let idx = thread::index_1d();
let i = idx.get();
if i < n as usize {
let (b, elem) = split(i, nstates);
let value = src[broadcast_src(b, src_stride, src_nbatch, nbatch, elem)];
if let Some(elem) = ret.get_mut(idx) {
*elem = scalar * value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (y.len() == n))]
pub fn vec_batched_axpy<T: ScalarCuda>(
mut y: DisjointSlice<T>,
x: &[T],
alpha: &[T],
beta: T,
n: u32,
nstates: u32,
x_stride: u32,
x_nbatch: u32,
nbatch: u32,
) {
let idx = thread::index_1d();
let i = idx.get();
if i < n as usize {
let (b, elem) = split(i, nstates);
let xv = x[broadcast_src(b, x_stride, x_nbatch, nbatch, elem)];
let a = alpha[b];
if let Some(elem) = y.get_mut(idx) {
*elem = a * xv + beta * *elem;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (lhs.len() >= (nbatch - 1) * lhs_stride + nstates))]
pub fn vec_copy<T: ScalarCuda>(
mut lhs: DisjointSlice<T>,
rhs: &[T],
n: u32,
nstates: u32,
lhs_stride: u32,
rhs_stride: u32,
rhs_nbatch: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, elem) = split(i, nstates);
let value = rhs[broadcast_src(b, rhs_stride, rhs_nbatch, nbatch, elem)];
let li = b * lhs_stride as usize + elem;
if li < lhs.len() {
unsafe {
*lhs.as_mut_ptr().add(li) = value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (lhs.len() >= (nbatch - 1) * lhs_stride + nstates))]
pub fn vec_add_assign<T: ScalarCuda>(
mut lhs: DisjointSlice<T>,
rhs: &[T],
n: u32,
nstates: u32,
lhs_stride: u32,
rhs_stride: u32,
rhs_nbatch: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, elem) = split(i, nstates);
let value = rhs[broadcast_src(b, rhs_stride, rhs_nbatch, nbatch, elem)];
let li = b * lhs_stride as usize + elem;
if li < lhs.len() {
unsafe {
let p = lhs.as_mut_ptr().add(li);
*p += value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (lhs.len() >= (nbatch - 1) * lhs_stride + nstates))]
pub fn vec_sub_assign<T: ScalarCuda>(
mut lhs: DisjointSlice<T>,
rhs: &[T],
n: u32,
nstates: u32,
lhs_stride: u32,
rhs_stride: u32,
rhs_nbatch: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, elem) = split(i, nstates);
let value = rhs[broadcast_src(b, rhs_stride, rhs_nbatch, nbatch, elem)];
let li = b * lhs_stride as usize + elem;
if li < lhs.len() {
unsafe {
let p = lhs.as_mut_ptr().add(li);
*p -= value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (lhs.len() >= (nbatch - 1) * lhs_stride + nstates))]
#[allow(unused_variables)]
pub fn vec_mul_assign_scalar<T: ScalarCuda>(
mut lhs: DisjointSlice<T>,
scalar: T,
n: u32,
nstates: u32,
lhs_stride: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, elem) = split(i, nstates);
let li = b * lhs_stride as usize + elem;
if li < lhs.len() {
unsafe {
let p = lhs.as_mut_ptr().add(li);
*p *= scalar;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (y.len() >= (nbatch - 1) * y_stride + nstates))]
pub fn vec_axpy<T: ScalarCuda>(
mut y: DisjointSlice<T>,
x: &[T],
alpha: T,
beta: T,
n: u32,
nstates: u32,
y_stride: u32,
x_stride: u32,
x_nbatch: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, elem) = split(i, nstates);
let xv = x[broadcast_src(b, x_stride, x_nbatch, nbatch, elem)];
let yi = b * y_stride as usize + elem;
if yi < y.len() {
unsafe {
let p = y.as_mut_ptr().add(yi);
*p = alpha * xv + beta * *p;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn vec_gather<T: ScalarCuda>(
mut dest: DisjointSlice<T>,
src: &[T],
indices: &[i32],
n: u32,
nindices: u32,
dest_stride: u32,
src_stride: u32,
src_nbatch: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, j) = split(i, nindices);
let value = src[broadcast_src(b, src_stride, src_nbatch, nbatch, indices[j] as usize)];
let di = b * dest_stride as usize + j;
if di < dest.len() {
unsafe {
*dest.as_mut_ptr().add(di) = value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn vec_scatter<T: ScalarCuda>(
mut dest: DisjointSlice<T>,
src: &[T],
indices: &[i32],
n: u32,
nindices: u32,
dest_stride: u32,
src_stride: u32,
src_nbatch: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, j) = split(i, nindices);
let value = src[broadcast_src(b, src_stride, src_nbatch, nbatch, j)];
let di = b * dest_stride as usize + indices[j] as usize;
if di < dest.len() {
unsafe {
*dest.as_mut_ptr().add(di) = value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn vec_copy_from_indices<T: ScalarCuda>(
mut dest: DisjointSlice<T>,
src: &[T],
indices: &[i32],
n: u32,
nindices: u32,
dest_stride: u32,
src_stride: u32,
src_nbatch: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, j) = split(i, nindices);
let index = indices[j] as usize;
let value = src[broadcast_src(b, src_stride, src_nbatch, nbatch, index)];
let di = b * dest_stride as usize + index;
if di < dest.len() {
unsafe {
*dest.as_mut_ptr().add(di) = value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn vec_assign_at_indices<T: ScalarCuda>(
mut dest: DisjointSlice<T>,
indices: &[i32],
value: T,
n: u32,
nindices: u32,
dest_stride: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, j) = split(i, nindices);
let di = b * dest_stride as usize + indices[j] as usize;
if di < dest.len() {
unsafe {
*dest.as_mut_ptr().add(di) = value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (out.len() == 1,
partials.len() >= blocks_per_lane * nbatch,
x.len() >= (nbatch - 1) * x_stride + nstates))]
pub fn vec_norm<T: ScalarCuda>(
out: &[DeviceAtomicU64],
mut partials: DisjointSlice<T>,
x: &[T],
nstates: u32,
nbatch: u32,
x_stride: u32,
blocks_per_lane: u32,
) {
let (mut b, blk, lane_step, step) = lane_loop(blocks_per_lane);
while b < nbatch as usize {
let mut local = T::zero();
let mut i = lane_start(blk);
while i < nstates as usize {
let v = x[b * x_stride as usize + i];
local += v * v;
i += step;
}
publish_block_sum(
out,
&mut partials,
blocks_per_lane,
b * blocks_per_lane as usize + blk,
local,
);
b += lane_step;
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (out.len() == 1,
x.len() >= (nbatch - 1) * x_stride + nstates))]
pub fn vec_norm_small<T: ScalarCuda>(
out: &[DeviceAtomicU64],
x: &[T],
nstates: u32,
nbatch: u32,
x_stride: u32,
cols_per_block: u32,
) {
let tid = thread::threadIdx_x() as usize;
let (first, nstates, cols) = lane_block(nstates, cols_per_block);
let term = match lane_element(first, nstates, cols, nbatch, tid) {
Some((b, elem)) => {
let v = x[b * x_stride as usize + elem];
v * v
}
None => T::zero(),
};
block_max_into(out, small_lane_sum(term, first, nstates, cols, nbatch));
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (out.len() == 1,
partials.len() >= blocks_per_lane * nbatch,
x.len() >= (nbatch - 1) * x_stride + nstates))]
pub fn vec_norm_lk<T: ScalarCuda>(
out: &[DeviceAtomicU64],
mut partials: DisjointSlice<T>,
x: &[T],
nstates: u32,
nbatch: u32,
x_stride: u32,
blocks_per_lane: u32,
k: i32,
) {
let (mut b, blk, lane_step, step) = lane_loop(blocks_per_lane);
while b < nbatch as usize {
let mut local = T::zero();
let mut i = lane_start(blk);
while i < nstates as usize {
local += x[b * x_stride as usize + i].abs().pow(k);
i += step;
}
publish_block_sum(
out,
&mut partials,
blocks_per_lane,
b * blocks_per_lane as usize + blk,
local,
);
b += lane_step;
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (out.len() == 1,
x.len() >= (nbatch - 1) * x_stride + nstates))]
pub fn vec_norm_lk_small<T: ScalarCuda>(
out: &[DeviceAtomicU64],
x: &[T],
nstates: u32,
nbatch: u32,
x_stride: u32,
cols_per_block: u32,
k: i32,
) {
let tid = thread::threadIdx_x() as usize;
let (first, nstates, cols) = lane_block(nstates, cols_per_block);
let term = match lane_element(first, nstates, cols, nbatch, tid) {
Some((b, elem)) => x[b * x_stride as usize + elem].abs().pow(k),
None => T::zero(),
};
block_max_into(out, small_lane_sum(term, first, nstates, cols, nbatch));
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (out.len() == 1,
partials.len() >= blocks_per_lane * nbatch,
y.len() >= (nbatch - 1) * y_stride + nstates))]
pub fn vec_squared_norm<T: ScalarCuda>(
out: &[DeviceAtomicU64],
mut partials: DisjointSlice<T>,
y: &[T],
y0: &[T],
atol: &[T],
rtol: T,
nstates: u32,
nbatch: u32,
y_stride: u32,
y0_stride: u32,
y0_nbatch: u32,
atol_stride: u32,
atol_nbatch: u32,
blocks_per_lane: u32,
) {
let (mut b, blk, lane_step, step) = lane_loop(blocks_per_lane);
while b < nbatch as usize {
let mut local = T::zero();
let mut i = lane_start(blk);
while i < nstates as usize {
let denom = y0[broadcast_src(b, y0_stride, y0_nbatch, nbatch, i)].abs() * rtol
+ atol[broadcast_src(b, atol_stride, atol_nbatch, nbatch, i)];
let ratio = y[b * y_stride as usize + i] / denom;
local += ratio * ratio;
i += step;
}
publish_block_sum(
out,
&mut partials,
blocks_per_lane,
b * blocks_per_lane as usize + blk,
local,
);
b += lane_step;
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (out.len() == 1,
y.len() >= (nbatch - 1) * y_stride + nstates))]
pub fn vec_squared_norm_small<T: ScalarCuda>(
out: &[DeviceAtomicU64],
y: &[T],
y0: &[T],
atol: &[T],
rtol: T,
nstates: u32,
nbatch: u32,
y_stride: u32,
y0_stride: u32,
y0_nbatch: u32,
atol_stride: u32,
atol_nbatch: u32,
cols_per_block: u32,
) {
let tid = thread::threadIdx_x() as usize;
let (first, nstates_u, cols) = lane_block(nstates, cols_per_block);
let term = match lane_element(first, nstates_u, cols, nbatch, tid) {
Some((b, elem)) => {
let denom = y0[broadcast_src(b, y0_stride, y0_nbatch, nbatch, elem)].abs() * rtol
+ atol[broadcast_src(b, atol_stride, atol_nbatch, nbatch, elem)];
let ratio = y[b * y_stride as usize + elem] / denom;
ratio * ratio
}
None => T::zero(),
};
block_max_into(out, small_lane_sum(term, first, nstates_u, cols, nbatch));
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1),
requires = (out.len() == 1,
partials.len() >= blocks_per_lane * nbatch))]
pub fn lane_sum_max<T: ScalarCuda>(
out: &[DeviceAtomicU64],
partials: &[T],
nbatch: u32,
blocks_per_lane: u32,
) {
let bpl = blocks_per_lane as usize;
let step = grid_stride();
let mut b = thread::index_1d().get();
let mut local = T::zero();
while b < nbatch as usize {
let mut sum = T::zero();
let base = b * bpl;
for j in 0..bpl {
sum += partials[base + j];
}
if sum > local {
local = sum;
}
b += step;
}
block_max_into(out, local);
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 2, block = (256, 1, 1))]
pub fn vec_root_finding<T: ScalarCuda>(
mut max_vals: DisjointSlice<T, Runtime2DIndex>,
mut max_idxs: DisjointSlice<i32, Runtime2DIndex>,
mut flags: DisjointSlice<i32, Runtime2DIndex>,
g0: &[T],
g1: &[T],
nstates: u32,
nbatch: u32,
g0_stride: u32,
g1_stride: u32,
g1_nbatch: u32,
) {
static mut SVALS: SharedArray<f64, { BLOCK_SIZE as usize }> = SharedArray::UNINIT;
let svals: *mut T = shared_as(unsafe { SharedArray::as_raw_mut_ptr(&raw mut SVALS) });
static mut SIDXS: SharedArray<i32, { BLOCK_SIZE as usize }> = SharedArray::UNINIT;
static mut SFLAGS: SharedArray<i32, { BLOCK_SIZE as usize }> = SharedArray::UNINIT;
let b = thread::index_2d_row();
let mut local_max = T::zero();
let mut local_idx = -1i32;
let mut local_flag = 0i32;
let mut i = thread::index_2d_col();
let step = grid_stride();
while i < nstates as usize {
let v0 = g0[b * g0_stride as usize + i];
let v1 = g1[broadcast_src(b, g1_stride, g1_nbatch, nbatch, i)];
if v1 == T::zero() {
local_flag = 1;
}
if v0 * v1 < T::zero() {
let val = (v1 / (v1 - v0)).abs();
if val > local_max {
local_max = val;
local_idx = i as i32;
}
}
i += step;
}
let tid = thread::threadIdx_x() as usize;
unsafe {
*svals.add(tid) = local_max;
SIDXS[tid] = local_idx;
SFLAGS[tid] = local_flag;
}
thread::sync_threads();
let mut s = BLOCK_SIZE as usize / 2;
while s > 0 {
if tid < s {
unsafe {
if *svals.add(tid) < *svals.add(tid + s) {
*svals.add(tid) = *svals.add(tid + s);
SIDXS[tid] = SIDXS[tid + s];
}
if SFLAGS[tid + s] != 0 {
SFLAGS[tid] = 1;
}
}
}
thread::sync_threads();
s /= 2;
}
if tid == 0 {
let slot = block_slot(b);
unsafe {
*max_vals.as_mut_ptr().add(slot) = *svals.add(0);
*max_idxs.as_mut_ptr().add(slot) = SIDXS[0];
*flags.as_mut_ptr().add(slot) = SFLAGS[0];
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (diag.len() == n))]
pub fn mat_get_diagonal<T: ScalarCuda>(
mut diag: DisjointSlice<T>,
mat: &[T],
n: u32,
nrows: u32,
mat_stride: u32,
mat_nbatch: u32,
nbatch: u32,
) {
let idx = thread::index_1d();
let i = idx.get();
if i < n as usize {
let (b, row) = split(i, nrows);
let mi = row * nrows as usize + row;
let value = mat[broadcast_src(b, mat_stride, mat_nbatch, nbatch, mi)];
if let Some(elem) = diag.get_mut(idx) {
*elem = value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn mat_from_diagonal<T: ScalarCuda>(
mut mat: DisjointSlice<T>,
diag: &[T],
n: u32,
nrows: u32,
mat_stride: u32,
diag_stride: u32,
diag_nbatch: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, row) = split(i, nrows);
let value = diag[broadcast_src(b, diag_stride, diag_nbatch, nbatch, row)];
let mi = b * mat_stride as usize + row * nrows as usize + row;
if mi < mat.len() {
unsafe {
*mat.as_mut_ptr().add(mi) = value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn mat_set_data_with_indices<T: ScalarCuda>(
mut dest: DisjointSlice<T>,
src: &[T],
dst_indices: &[i32],
src_indices: &[i32],
n: u32,
nindices: u32,
dest_stride: u32,
src_stride: u32,
src_nbatch: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, j) = split(i, nindices);
let value = src[broadcast_src(b, src_stride, src_nbatch, nbatch, src_indices[j] as usize)];
let di = b * dest_stride as usize + dst_indices[j] as usize;
if di < dest.len() {
unsafe {
*dest.as_mut_ptr().add(di) = value;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (dest.len() == n))]
pub fn mat_scale_add_assign<T: ScalarCuda>(
mut dest: DisjointSlice<T>,
x: &[T],
y: &[T],
beta: T,
n: u32,
nstates: u32,
x_stride: u32,
x_nbatch: u32,
y_stride: u32,
y_nbatch: u32,
nbatch: u32,
) {
let idx = thread::index_1d();
let i = idx.get();
if i < n as usize {
let (b, elem) = split(i, nstates);
let xv = x[broadcast_src(b, x_stride, x_nbatch, nbatch, elem)];
let yv = y[broadcast_src(b, y_stride, y_nbatch, nbatch, elem)];
if let Some(elem) = dest.get_mut(idx) {
*elem = xv + beta * yv;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (y.len() == n))]
pub fn gemv_cols<T: ScalarCuda>(
mut y: DisjointSlice<T>,
mat: &[T],
w: [T; MAX_SMALL_COLS],
nc: u32,
alpha: T,
beta: T,
n: u32,
nstates: u32,
nrows: u32,
mat_stride: u32,
mat_nbatch: u32,
nbatch: u32,
) {
let idx = thread::index_1d();
let i = idx.get();
if i >= n as usize {
return;
}
let (b, row) = split(i, nstates);
if row >= nrows as usize {
return;
}
let base = broadcast_src(b, mat_stride, mat_nbatch, nbatch, row);
let mut acc = T::zero();
for k in 0..nc as usize {
acc += w[k] * mat[base + k * nrows as usize];
}
if let Some(elem) = y.get_mut(idx) {
*elem = if beta == T::zero() {
alpha * acc
} else {
alpha * acc + beta * *elem
};
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
#[allow(clippy::needless_range_loop)]
pub fn mul_cols_by<T: ScalarCuda>(
mut mat: DisjointSlice<T>,
rhs: [T; MAX_SMALL_COLS_SQ],
n: u32,
ncols: u32,
nrows: u32,
mat_stride: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let ncols = ncols as usize;
if ncols == 0 {
return;
}
let (b, row) = split(i, nrows);
let base = b * mat_stride as usize + row;
let stride = nrows as usize;
if base + (ncols - 1) * stride >= mat.len() {
return;
}
let ptr = mat.as_mut_ptr();
let mut old = [T::zero(); MAX_SMALL_COLS];
unsafe {
for l in 0..ncols {
old[l] = *ptr.add(base + l * stride);
}
for j in 0..ncols {
let mut acc = T::zero();
for l in 0..ncols {
acc += old[l] * rhs[j * ncols + l];
}
*ptr.add(base + j * stride) = acc;
}
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn backward_diff_update<T: ScalarCuda>(
mut diff: DisjointSlice<T>,
d: &[T],
order: u32,
n: u32,
nrows: u32,
diff_stride: u32,
d_stride: u32,
d_nbatch: u32,
nbatch: u32,
) {
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, elem) = split(i, nrows);
let dv = d[broadcast_src(b, d_stride, d_nbatch, nbatch, elem)];
let base = b * diff_stride as usize + elem;
let stride = nrows as usize;
let order = order as usize;
if base + (order + 2) * stride >= diff.len() {
return;
}
let ptr = diff.as_mut_ptr();
unsafe {
let mut carry = dv - *ptr.add(base + (order + 1) * stride);
*ptr.add(base + (order + 2) * stride) = carry;
let mut i = order + 1;
loop {
carry += *ptr.add(base + i * stride);
*ptr.add(base + i * stride) = carry;
if i == 0 {
break;
}
i -= 1;
}
}
}
#[inline(always)]
fn shared_as<T: ScalarCuda>(p: *mut f64) -> *mut T {
const {
assert!(
core::mem::size_of::<T>() <= core::mem::size_of::<f64>()
&& core::mem::align_of::<T>() <= core::mem::align_of::<f64>()
)
};
p.cast()
}
fn atomic_max_into<T: ScalarCuda>(out: &[DeviceAtomicU64], value: T) {
if value > T::zero() {
out[0].fetch_max(value.to_max_bits(), RELAXED);
}
}
fn block_max_into<T: ScalarCuda>(out: &[DeviceAtomicU64], value: T) {
static mut SMAX: SharedArray<f64, { BLOCK_SIZE as usize }> = SharedArray::UNINIT;
let smax: *mut T = shared_as(unsafe { SharedArray::as_raw_mut_ptr(&raw mut SMAX) });
let tid = thread::threadIdx_x() as usize;
unsafe {
*smax.add(tid) = value;
}
thread::sync_threads();
let mut s = BLOCK_SIZE as usize / 2;
while s > 0 {
if tid < s {
unsafe {
if *smax.add(tid) < *smax.add(tid + s) {
*smax.add(tid) = *smax.add(tid + s);
}
}
}
thread::sync_threads();
s /= 2;
}
if tid == 0 {
atomic_max_into(out, unsafe { *smax.add(0) });
}
}
fn publish_block_sum<T: ScalarCuda>(
out: &[DeviceAtomicU64],
partials: &mut DisjointSlice<T>,
blocks_per_lane: u32,
slot: usize,
local: T,
) {
if let Some(total) = block_sum(local) {
if blocks_per_lane == 1 {
atomic_max_into(out, total);
} else {
unsafe {
*partials.as_mut_ptr().add(slot) = total;
}
}
}
}
fn small_lane_sum<T: ScalarCuda>(
term: T,
first: usize,
nstates: usize,
cols: usize,
nbatch: u32,
) -> T {
static mut SDATA: SharedArray<f64, { BLOCK_SIZE as usize }> = SharedArray::UNINIT;
let sdata: *mut T = shared_as(unsafe { SharedArray::as_raw_mut_ptr(&raw mut SDATA) });
let tid = thread::threadIdx_x() as usize;
unsafe {
*sdata.add(tid) = term;
}
thread::sync_threads();
let mut sum = T::zero();
if tid < cols && first + tid < nbatch as usize {
let base = tid * nstates;
for j in 0..nstates {
sum += unsafe { *sdata.add(base + j) };
}
}
sum
}
fn block_sum<T: ScalarCuda>(local: T) -> Option<T> {
static mut SWARP: SharedArray<f64, WARPS_PER_BLOCK> = SharedArray::UNINIT;
let swarp: *mut T = shared_as(unsafe { SharedArray::as_raw_mut_ptr(&raw mut SWARP) });
let lane = warp::lane_id() as usize;
let w = warp::warp_id() as usize;
let warp_total = local.warp_reduce_sum();
if lane == 0 {
unsafe {
*swarp.add(w) = warp_total;
}
}
thread::sync_threads();
let mut total = None;
if w == 0 {
let slot = if lane < WARPS_PER_BLOCK {
unsafe { *swarp.add(lane) }
} else {
T::zero()
};
let sum = slot.warp_reduce_sum();
if lane == 0 {
total = Some(sum);
}
}
thread::sync_threads();
total
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn vec_for_each_batch<T: ScalarCuda, const M: usize, const N: usize, F>(
f: F,
outs: LaneArgsMut<T, M>,
ins: LaneArgs<T, N>,
nbatch: u32,
) where
F: Fn([&mut [T]; M], [&[T]; N], usize) + Copy,
{
let b = thread::index_1d().get();
if b >= nbatch as usize {
return;
}
let o = core::array::from_fn(|i| unsafe {
let n = outs.nstates[i] as usize;
core::slice::from_raw_parts_mut(outs.ptr[i].add(b * n), n)
});
let a = core::array::from_fn(|i| unsafe {
let n = ins.nstates[i] as usize;
let base = broadcast_src(b, ins.nstates[i], ins.nbatch[i], nbatch, 0);
core::slice::from_raw_parts(ins.ptr[i].add(base), n)
});
f(o, a, b);
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn vec_for_each_elem<T: ScalarCuda, const M: usize, const N: usize, F>(
f: F,
outs: LaneArgsMut<T, M>,
ins: LaneArgs<T, N>,
n: u32,
nstates: u32,
nbatch: u32,
) where
F: Fn([&mut T; M], [&[T]; N], usize, usize) + Copy,
{
let i = thread::index_1d().get();
if i >= n as usize {
return;
}
let (b, elem) = split(i, nstates);
let o =
core::array::from_fn(|k| unsafe { &mut *outs.ptr[k].add(b * nstates as usize + elem) });
let a = core::array::from_fn(|k| unsafe {
let len = ins.nstates[k] as usize;
let base = broadcast_src(b, ins.nstates[k], ins.nbatch[k], nbatch, 0);
core::slice::from_raw_parts(ins.ptr[k].add(base), len)
});
f(o, a, b, elem);
}
fn warp_reduce<T: ScalarCuda, G>(mut val: T, combine: G) -> T
where
G: Fn(T, T) -> T + Copy,
{
val = combine(val, val.shuffle_xor(16));
val = combine(val, val.shuffle_xor(8));
val = combine(val, val.shuffle_xor(4));
val = combine(val, val.shuffle_xor(2));
val = combine(val, val.shuffle_xor(1));
val
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1), requires = (dest.len() == nstates))]
pub fn vec_reduce_batch<T: ScalarCuda, const N: usize, F, G>(
f: F,
g: G,
mut dest: DisjointSlice<T>,
ins: LaneArgs<T, N>,
init: T,
nstates: u32,
nbatch: u32,
) where
F: Fn([&[T]; N], usize, usize) -> T + Copy,
G: Fn(T, T) -> T + Copy,
{
let idx = thread::index_1d();
let i = idx.get();
if i >= nstates as usize {
return;
}
let mut acc = init;
let mut b = 0usize;
while b < nbatch as usize {
let a = core::array::from_fn(|k| unsafe {
let len = ins.nstates[k] as usize;
let base = broadcast_src(b, ins.nstates[k], ins.nbatch[k], nbatch, 0);
core::slice::from_raw_parts(ins.ptr[k].add(base), len)
});
acc = g(acc, f(a, b, i));
b += 1;
}
if let Some(elem) = dest.get_mut(idx) {
*elem = acc;
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn vec_reduce_batch_small<T: ScalarCuda, const N: usize, F, G>(
f: F,
g: G,
mut dest: DisjointSlice<T>,
ins: LaneArgs<T, N>,
init: T,
nstates: u32,
nbatch: u32,
) where
F: Fn([&[T]; N], usize, usize) -> T + Copy,
G: Fn(T, T) -> T + Copy,
{
let warps = thread::gridDim_x() as usize * WARPS_PER_BLOCK;
let lane = warp::lane_id() as usize;
let mut i = thread::blockIdx_x() as usize * WARPS_PER_BLOCK + warp::warp_id() as usize;
while i < nstates as usize {
let mut acc = init;
let mut b = lane;
while b < nbatch as usize {
let a = core::array::from_fn(|k| unsafe {
let len = ins.nstates[k] as usize;
let base = broadcast_src(b, ins.nstates[k], ins.nbatch[k], nbatch, 0);
core::slice::from_raw_parts(ins.ptr[k].add(base), len)
});
acc = g(acc, f(a, b, i));
b += 32;
}
let total = warp_reduce(acc, g);
if lane == 0 && i < dest.len() {
unsafe {
*dest.as_mut_ptr().add(i) = total;
}
}
i += warps;
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn vec_reduce_elem<T: ScalarCuda, const N: usize, F, G>(
f: F,
g: G,
mut dest: DisjointSlice<T>,
ins: LaneArgs<T, N>,
init: T,
nstates: u32,
nbatch: u32,
) where
F: Fn([&[T]; N], usize, usize) -> T + Copy,
G: Fn(T, T) -> T + Copy,
{
let warps = thread::gridDim_x() as usize * WARPS_PER_BLOCK;
let lane = warp::lane_id() as usize;
let mut b = thread::blockIdx_x() as usize * WARPS_PER_BLOCK + warp::warp_id() as usize;
while b < nbatch as usize {
let a = core::array::from_fn(|k| unsafe {
let len = ins.nstates[k] as usize;
let base = broadcast_src(b, ins.nstates[k], ins.nbatch[k], nbatch, 0);
core::slice::from_raw_parts(ins.ptr[k].add(base), len)
});
let mut acc = init;
let mut i = lane;
while i < nstates as usize {
acc = g(acc, f(a, b, i));
i += 32;
}
let total = warp_reduce(acc, g);
if lane == 0 && b < dest.len() {
unsafe {
*dest.as_mut_ptr().add(b) = total;
}
}
b += warps;
}
}
#[kernel]
#[launch_bounds(256)]
#[launch_contract(domain = 1, block = (256, 1, 1))]
pub fn vec_reduce_elem_small<T: ScalarCuda, const N: usize, F, G>(
f: F,
g: G,
mut dest: DisjointSlice<T>,
ins: LaneArgs<T, N>,
init: T,
nstates: u32,
nbatch: u32,
) where
F: Fn([&[T]; N], usize, usize) -> T + Copy,
G: Fn(T, T) -> T + Copy,
{
let idx = thread::index_1d();
let b = idx.get();
if b >= nbatch as usize {
return;
}
let a = core::array::from_fn(|k| unsafe {
let len = ins.nstates[k] as usize;
let base = broadcast_src(b, ins.nstates[k], ins.nbatch[k], nbatch, 0);
core::slice::from_raw_parts(ins.ptr[k].add(base), len)
});
let mut acc = init;
for i in 0..nstates as usize {
acc = g(acc, f(a, b, i));
}
if let Some(elem) = dest.get_mut(idx) {
*elem = acc;
}
}
}