use cudarc::driver::{
CudaFunction, CudaSlice, CudaStream, CudaView, CudaViewMut, LaunchConfig, PushKernelArg,
};
use super::{
Batches, Coverage, CoverageEntry, FiniteContract, InfinityContract, Maths, NanContract, Phases,
PlanError, SignedZeroContract, SincCandidate, SincInputs, SincOutput, SincPin, SincSpec,
SpecialValues,
};
use crate::inference::cuda::error::check_len;
use crate::inference::cuda::{CudaError, CudaMath, CudaRuntime, LoadedKernels};
const SPK_SINCNET_PACK_FILTERS: &str = "spk_sincnet_pack_filters";
const SPK_SINCNET_CONV_ABS_POOL: &str = "spk_sincnet_conv_abs_pool";
pub(crate) const REQUIRED_KERNELS: [&str; 2] =
[SPK_SINCNET_PACK_FILTERS, SPK_SINCNET_CONV_ABS_POOL];
const LAYER: &str = "sincnet.conv0.abs_pool";
const CONTEXT: &str = "sinc producer";
const CHANNELS: usize = 80;
const TAPS: usize = 251;
const STRIDE: usize = 10;
const POOL: usize = 3;
const GROUP: usize = 16;
const TILE: usize = 256;
const THREADS: u32 = 256;
const IMPLEMENTED: Coverage = Coverage(&[CoverageEntry {
layers: &[LAYER],
batches: Batches::All,
maths: Maths::All,
}]);
const PACK_THREADS: u32 = 256;
#[derive(Debug)]
pub(crate) struct Oxide {
produce: CudaFunction,
packed: CudaSlice<f32>,
batch: usize,
samples: usize,
pooled: usize,
}
impl SincCandidate for Oxide {
const COVERAGE: Coverage = Coverage(&[CoverageEntry {
layers: &[LAYER],
batches: Batches::All,
maths: Maths::Only(&[CudaMath::Fp32]),
}]);
const SPECIAL_VALUES: SpecialValues = SpecialValues {
finite: FiniteContract::AbsoluteSum { headroom: 2 },
nan: NanContract::PoolingIgnores,
infinity: InfinityContract::Ieee,
signed_zero: SignedZeroContract::Positive,
};
const OUTPUT: SincOutput = SincOutput::Pooled;
fn implemented_pin(_spec: &SincSpec<'_>) -> Result<SincPin, PlanError> {
Ok(SincPin::ConvAbsPool)
}
fn plan(
runtime: &CudaRuntime,
kernels: &LoadedKernels,
spec: SincSpec<'_>,
pin: SincPin,
) -> Result<Self, PlanError> {
let SincPin::ConvAbsPool = pin;
check_len(CONTEXT, CHANNELS * TAPS, spec.filters.len())?;
check_len(
CONTEXT,
(spec.samples.saturating_sub(TAPS) / STRIDE + 1) / POOL,
spec.pooled,
)?;
let mut packed = runtime.stream().alloc_zeros(CHANNELS * TAPS)?;
pack(
runtime.stream(),
&kernels.function(SPK_SINCNET_PACK_FILTERS)?,
&spec.filters.as_view(),
&mut packed,
)?;
runtime.synchronize()?;
Ok(Self {
produce: kernels.function(SPK_SINCNET_CONV_ABS_POOL)?,
packed,
batch: spec.batch,
samples: spec.samples,
pooled: spec.pooled,
})
}
fn enqueue(
&self,
inputs: SincInputs<'_, '_>,
output: &mut CudaViewMut<'_, f32>,
_phases: &Phases,
stream: &CudaStream,
) -> Result<(), CudaError> {
check_len(CONTEXT, self.batch * self.samples, inputs.waveform.len())?;
check_len(CONTEXT, self.batch * CHANNELS * self.pooled, output.len())?;
self.produce(stream, inputs.waveform, output)
}
}
fn pack(
stream: &CudaStream,
function: &CudaFunction,
filters: &CudaView<'_, f32>,
packed: &mut CudaSlice<f32>,
) -> Result<(), CudaError> {
let elements = to_u32(packed.len())?;
let filters_len = filters.len() as u64;
let packed_len = packed.len() as u64;
let mut launch = stream.launch_builder(function);
launch
.arg(filters)
.arg(&filters_len)
.arg(packed)
.arg(&packed_len);
let config = LaunchConfig {
grid_dim: (elements.div_ceil(PACK_THREADS), 1, 1),
block_dim: (PACK_THREADS, 1, 1),
shared_mem_bytes: 0,
};
unsafe { launch.launch(config) }?;
Ok(())
}
impl Oxide {
fn produce(
&self,
stream: &CudaStream,
wave: &CudaView<'_, f32>,
output: &mut CudaViewMut<'_, f32>,
) -> Result<(), CudaError> {
let tiles = to_u32(self.pooled.div_ceil(TILE))?;
let groups = to_u32(CHANNELS / GROUP)?;
let batch = to_u32(self.batch)?;
let samples = to_u32(self.samples)?;
let pool_len = to_u32(self.pooled)?;
let wave_len = wave.len() as u64;
let quads = (self.packed.len() / 4) as u64;
let output_len = output.len() as u64;
let mut launch = stream.launch_builder(&self.produce);
launch
.arg(wave)
.arg(&wave_len)
.arg(&self.packed)
.arg(&quads)
.arg(output)
.arg(&output_len)
.arg(&samples)
.arg(&pool_len);
let config = LaunchConfig {
grid_dim: (tiles, groups, batch),
block_dim: (THREADS, 1, 1),
shared_mem_bytes: 0,
};
unsafe { launch.launch(config) }?;
Ok(())
}
}
fn to_u32(value: usize) -> Result<u32, CudaError> {
u32::try_from(value).map_err(|_| CudaError::DimensionOverflow {
context: CONTEXT,
value,
})
}
impl super::DriverCandidate for Oxide {
const AREA: super::KernelModule = super::KernelModule::Sincnet;
fn driver_coverage(
_tier: super::PtxTier,
_device: &super::super::device::DeviceAttributes,
_fp16: super::Fp16Policy,
) -> Coverage {
IMPLEMENTED
}
fn driver_pin(
_boundary: super::super::implementation::BoundaryId,
_batch: usize,
_math: CudaMath,
_device: &super::super::device::DeviceAttributes,
_tier: super::PtxTier,
_fp16: super::Fp16Policy,
) -> Result<super::ConfigPin, PlanError> {
Ok(super::ConfigPin::Sinc(SincPin::ConvAbsPool))
}
}