use std::sync::Arc;
use cudarc::driver::{CudaSlice, CudaStream, CudaView, CudaViewMut, LaunchConfig, PushKernelArg};
use super::columns::grow;
use super::error::GpuError;
use super::module::KernelSet;
const SCAN_BLOCK: usize = 4096;
const SCAN_THREADS: u32 = 1024;
pub(crate) struct ScanScratch {
block_sum: CudaSlice<u32>,
block_max: CudaSlice<u32>,
}
impl ScanScratch {
pub(crate) fn new(stream: &Arc<CudaStream>) -> Result<Self, GpuError> {
Ok(Self { block_sum: stream.alloc_zeros(1)?, block_max: stream.alloc_zeros(1)? })
}
}
pub(crate) fn exclusive_scan(
stream: &Arc<CudaStream>,
kernels: &KernelSet,
input: &CudaView<'_, u32>,
out: &mut CudaViewMut<'_, u32>,
n: usize,
scratch: &mut ScanScratch,
total_max: &mut CudaSlice<u32>,
) -> Result<(), GpuError> {
let nb = n.div_ceil(SCAN_BLOCK).max(1);
if nb > SCAN_BLOCK {
return Err(GpuError::Unsupported("scan of more than 2^24 elements"));
}
debug_assert!(input.len() >= n && out.len() > n && total_max.len() >= 2);
let ordinal = stream.context().ordinal() as u32;
grow(stream, &mut scratch.block_sum, nb, ordinal)?;
grow(stream, &mut scratch.block_max, nb, ordinal)?;
let block_sum = &mut scratch.block_sum;
let block_max = &mut scratch.block_max;
let (n32, nb32) = (n as u32, nb as u32);
let block = (SCAN_THREADS, 1, 1);
unsafe {
stream
.launch_builder(&kernels.scan_block)
.arg(input)
.arg(&mut *out)
.arg(&mut *block_sum)
.arg(&mut *block_max)
.arg(&n32)
.launch(LaunchConfig {
grid_dim: (nb32, 1, 1),
block_dim: block,
shared_mem_bytes: 0,
})?;
stream
.launch_builder(&kernels.scan_single)
.arg(&mut *block_sum)
.arg(&*block_max)
.arg(&nb32)
.arg(&mut *out)
.arg(&n32)
.arg(&mut *total_max)
.launch(LaunchConfig { grid_dim: (1, 1, 1), block_dim: block, shared_mem_bytes: 0 })?;
stream.launch_builder(&kernels.scan_add).arg(&mut *out).arg(&*block_sum).arg(&n32).launch(
LaunchConfig {
grid_dim: ((n as u32).div_ceil(SCAN_THREADS).max(1), 1, 1),
block_dim: block,
shared_mem_bytes: 0,
},
)?;
}
Ok(())
}