libmir-cuda 0.1.0

CUDA inference backend for libmir
use mircuda::{DeviceBuffer, Stream, bf16};
use runtime::kv::{BlockTable, KvWritePlan};

use super::{
    DenseScratch, DenseSwiGluConfig, DenseSwiGluWeights, DownProjection, GateUpBuffers,
    GateUpProjection,
};
use crate::{
    CudaBackend, DecodeAttentionBf16, Error, Result, RmsNormBf16,
    kernels::{ElementwiseBf16, PackedGatedBf16},
};

pub struct DecodeDenseSwiGlu {
    pub(in crate::backend) attention: DecodeAttentionBf16,
    pub(super) post_attention_norm: RmsNormBf16,
    pub(super) gate_up: GateUpProjection,
    pub(super) down: DownProjection,
    pub(super) activation: PackedGatedBf16,
    pub(super) hidden_ops: ElementwiseBf16,
    pub(super) scratch: DenseScratch,
    pub(super) stream: Stream,
    pub(super) config: DenseSwiGluConfig,
}

impl DecodeDenseSwiGlu {
    pub(in crate::backend) fn new_with_cache(
        backend: &CudaBackend,
        config: DenseSwiGluConfig,
        cache: crate::PagedKvCache,
        weights: DenseSwiGluWeights<'_>,
    ) -> Result<Self> {
        validate(config)?;
        let hidden = config.attention.hidden_size;
        let intermediate = config.intermediate_size;
        Ok(Self {
            attention: DecodeAttentionBf16::new_with_cache_and_weights(
                backend,
                config.attention,
                cache,
                Some(weights.attention),
            )?,
            post_attention_norm: RmsNormBf16::new(
                backend,
                1,
                hidden,
                config.attention.rms_norm_epsilon,
            )?,
            gate_up: GateUpProjection::new(backend, config, 1, Some(weights.gate_up))?,
            down: DownProjection::new(backend, config, 1, Some(weights.down))?,
            activation: PackedGatedBf16::compile(&backend.inner.compiler, 1, intermediate)?,
            hidden_ops: ElementwiseBf16::compile(&backend.inner.compiler, hidden)?,
            scratch: DenseScratch::new(backend, config, 1)?,
            stream: backend.inner.stream.clone(),
            config,
        })
    }

    pub fn execute(
        &mut self,
        input: &DeviceBuffer<bf16>,
        weights: DenseSwiGluWeights<'_>,
        write_plan: &KvWritePlan,
        table: &BlockTable,
        output: &mut DeviceBuffer<bf16>,
    ) -> Result<()> {
        self.attention.execute(
            input,
            weights.attention,
            write_plan,
            table,
            &mut self.scratch.attention,
        )?;
        self.hidden_ops.add(
            &self.stream,
            input,
            &self.scratch.attention,
            &mut self.scratch.residual,
        )?;
        let separate = self.gate_up.execute(
            &self.scratch.residual,
            &self.post_attention_norm,
            weights.post_attention_norm,
            weights.gate_up,
            &mut GateUpBuffers {
                normalized: &mut self.scratch.normalized,
                packed: &mut self.scratch.gate_up,
                separate: &mut self.scratch.gate_up_separate,
            },
        )?;
        let fused_down = separate
            && self.down.execute_gated(
                &self.scratch.gate_up_separate[0],
                &self.scratch.gate_up_separate[1],
                self.config.activation,
                weights.down,
                &mut self.scratch.mlp,
            )?;
        if separate && !fused_down {
            self.activation.execute_separate(
                &self.stream,
                &self.scratch.gate_up_separate[0],
                &self.scratch.gate_up_separate[1],
                &mut self.scratch.activated,
                self.config.activation.into(),
            )?;
        } else if !fused_down {
            self.activation.execute(
                &self.stream,
                &self.scratch.gate_up,
                &mut self.scratch.activated,
                self.config.activation.into(),
            )?;
        }
        if !fused_down {
            self.down
                .execute(&self.scratch.activated, weights.down, &mut self.scratch.mlp)?;
        }
        self.hidden_ops
            .add(&self.stream, &self.scratch.residual, &self.scratch.mlp, output)
    }
}

pub(super) fn validate(config: DenseSwiGluConfig) -> Result<()> {
    if config.intermediate_size == 0 {
        Err(Error::InvalidDecoderKernel("dense SwiGLU intermediate size is zero"))
    } else {
        Ok(())
    }
}