libmir-cuda 0.3.0

CUDA inference backend for libmir
use mircuda::{DeviceBuffer, bf16};

use super::super::super::{AffineSharedExpertMoeConfig, weights::AffineRoutedMoeWeights};
use crate::{
    AffineQuantizedConfig, AffineQuantizedPairTensors, CudaBackend, GatedActivation, Result,
    SelectedAffineGatedBf16Linear, SelectedAffinePairBf16Linear, SelectedAffineReduceBf16Linear,
    backend::tuning::AffineMoeExecution, kernels::ElementwiseBf16,
};

#[derive(Debug)]
pub(super) struct Candidate {
    pub(super) execution: AffineMoeExecution,
    plan: Plan,
    activation: GatedActivation,
    stream: mircuda::Stream,
}

#[derive(Debug)]
enum Plan {
    Fused {
        gated: SelectedAffineGatedBf16Linear,
        down: SelectedAffineReduceBf16Linear,
    },
    Separate {
        pair: SelectedAffinePairBf16Linear,
        activation: ElementwiseBf16,
        gate: DeviceBuffer<bf16>,
        up: DeviceBuffer<bf16>,
        down: SelectedAffineReduceBf16Linear,
    },
}

impl Candidate {
    pub(super) fn new(
        backend: &CudaBackend,
        config: AffineSharedExpertMoeConfig,
        tokens: usize,
        execution: AffineMoeExecution,
    ) -> Result<Self> {
        let gate = AffineQuantizedConfig::new(
            config.hidden_size,
            config.routed_intermediate_size,
            config.group_size,
            config.expert_bits,
        );
        let down_config = AffineQuantizedConfig::new(
            config.routed_intermediate_size,
            config.hidden_size,
            config.group_size,
            config.expert_bits,
        );
        let down = backend.prepare_batched_selected_affine_reduce_bf16_linear(
            tokens,
            down_config,
            config.expert_count,
            config.top_k,
        )?;
        let plan = match execution {
            AffineMoeExecution::FusedGated => Plan::Fused {
                gated: backend.prepare_batched_selected_affine_gated_bf16_linear(
                    tokens,
                    gate,
                    config.expert_count,
                    config.top_k,
                    config.activation,
                )?,
                down,
            },
            AffineMoeExecution::SeparatePair => {
                let pair = backend.prepare_batched_selected_affine_pair_bf16_linear(
                    tokens,
                    gate,
                    config.expert_count,
                    config.top_k,
                )?;
                let elements = pair.output_elements()?;
                Plan::Separate {
                    pair,
                    activation: ElementwiseBf16::compile(backend.compiler(), elements)?,
                    gate: backend.pool().allocate(backend.stream(), elements)?,
                    up: backend.pool().allocate(backend.stream(), elements)?,
                    down,
                }
            },
        };
        Ok(Self {
            execution,
            plan,
            activation: config.activation,
            stream: backend.stream().clone(),
        })
    }

    #[allow(clippy::too_many_arguments)]
    pub(super) fn execute(
        &mut self,
        input: &DeviceBuffer<bf16>,
        selected: &DeviceBuffer<u32>,
        routing: &DeviceBuffer<bf16>,
        weights: &AffineRoutedMoeWeights,
        intermediate: &mut DeviceBuffer<bf16>,
        output: &mut DeviceBuffer<bf16>,
    ) -> Result<()> {
        let pair = AffineQuantizedPairTensors {
            gate: weights.gate.tensors(),
            up: weights.up.tensors(),
        };
        match &mut self.plan {
            Plan::Fused { gated, down } => {
                gated.execute(input, selected, pair, intermediate)?;
                down.execute(intermediate, selected, routing, weights.down.tensors(), output)
            },
            Plan::Separate {
                pair: operation,
                activation,
                gate,
                up,
                down,
            } => {
                operation.execute(input, selected, pair, gate, up)?;
                activation.gated(&self.stream, gate, up, intermediate, self.activation.into())?;
                down.execute(intermediate, selected, routing, weights.down.tensors(), output)
            },
        }
    }
}