libmir-cuda 0.2.0

CUDA inference backend for libmir
use mircuda::{
    CompileOptions, Compiler, DeviceBuffer, LaunchConfig, Stream, TypedKernel, bf16, cuda_export,
    cuda_kernel_file,
};

use super::super::geometry::{narrow, product, require};
use crate::{Error, Result};

cuda_export!(
    ConvolutionKernel = "libmir_cuda_gated_delta_convolution_bf16"(
        input: &DeviceBuffer<bf16>, weight: &DeviceBuffer<bf16>,
        history: &DeviceBuffer<bf16>, output: &mut DeviceBuffer<bf16>,
        tokens: u32, channels: u32, kernel_size: u32,
    )
);
cuda_export!(
    HistoryKernel = "libmir_cuda_gated_delta_history_bf16"(
        input: &DeviceBuffer<bf16>, history: &DeviceBuffer<bf16>,
        next_history: &mut DeviceBuffer<bf16>, tokens: u32,
        channels: u32, kernel_size: u32,
    )
);

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GatedDeltaConvolutionSpec {
    pub tokens: usize,
    pub channels: usize,
    pub kernel_size: usize,
}

#[derive(Clone, Debug)]
pub struct GatedDeltaConvolution {
    convolution: TypedKernel<ConvolutionKernel>,
    history: TypedKernel<HistoryKernel>,
    spec: GatedDeltaConvolutionSpec,
}

impl GatedDeltaConvolution {
    pub fn compile(compiler: &Compiler, spec: GatedDeltaConvolutionSpec) -> Result<Self> {
        if spec.tokens == 0 || spec.channels == 0 || spec.kernel_size < 2 {
            return Err(Error::InvalidDecoderKernel("invalid Gated Delta convolution geometry"));
        }
        let source = cuda_kernel_file!("../../../kernels/gated_delta_bf16.cu");
        let module = compiler.compile(source, &CompileOptions::default())?;
        Ok(Self {
            convolution: module.kernel()?,
            history: module.kernel()?,
            spec,
        })
    }

    pub fn execute(
        &self,
        stream: &Stream,
        input: &DeviceBuffer<bf16>,
        weight: &DeviceBuffer<bf16>,
        history: &DeviceBuffer<bf16>,
        next_history: &mut DeviceBuffer<bf16>,
        output: &mut DeviceBuffer<bf16>,
    ) -> Result<()> {
        let values = product(self.spec.tokens, self.spec.channels)?;
        let history_values = self.history_elements()?;
        require("Gated Delta convolution input", values, input.len())?;
        require(
            "Gated Delta convolution weight",
            product(self.spec.channels, self.spec.kernel_size)?,
            weight.len(),
        )?;
        require("Gated Delta convolution history", history_values, history.len())?;
        require("Gated Delta next convolution history", history_values, next_history.len())?;
        require("Gated Delta convolution output", values, output.len())?;
        let arguments = (
            narrow(self.spec.tokens)?,
            narrow(self.spec.channels)?,
            narrow(self.spec.kernel_size)?,
        );
        self.convolution.launch(
            stream,
            launch(values)?,
            (input, weight, history, output, arguments.0, arguments.1, arguments.2),
        )?;
        Ok(self.history.launch(
            stream,
            launch(history_values)?,
            (input, history, next_history, arguments.0, arguments.1, arguments.2),
        )?)
    }

    pub fn history_elements(&self) -> Result<usize> {
        product(self.spec.kernel_size - 1, self.spec.channels)
    }
}

fn launch(elements: usize) -> Result<LaunchConfig> {
    Ok(LaunchConfig {
        grid: (narrow(elements.div_ceil(256))?, 1, 1),
        block: (256, 1, 1),
        shared_memory_bytes: 0,
    })
}