cubecl-matmul 0.8.1

CubeCL Matrix Multiplication Kernels Engine
Documentation
use crate::components::{
    InvalidConfigError, MatmulIdent, MatrixLayout, MatrixPrecision,
    global::{
        CopyMechanism, GlobalConfig,
        memory::{GlobalIterator, load_window_in_stage},
        read::AsyncFullLoadingStrategy,
    },
    stage::{StridedStage, StridedTilingLayout, TilingValidation},
};
use cubecl_core::prelude::*;
use cubecl_core::{self as cubecl, prelude::barrier::BarrierLevel};

use super::{AsyncLoadingJob, LoadingValidation};

#[derive(CubeType, Clone, Copy)]
/// Executes one memcpy_async call per contiguous slice.
/// The goal is to reduce the total number of memcpy_async calls, though it may result in idle threads.
pub struct AsyncFullMaximizeSliceLengthLoading {}

impl LoadingValidation for AsyncFullMaximizeSliceLengthLoading {
    fn check<C: GlobalConfig>(config: &C, ident: MatmulIdent) -> Result<(), InvalidConfigError> {
        StridedTilingLayout::check(config.global_memory_config(ident))?;

        Ok(())
    }
}

#[cube]
impl AsyncFullLoadingStrategy for AsyncFullMaximizeSliceLengthLoading {
    type TilingLayout = StridedTilingLayout;
    type Job<IP: MatrixPrecision> = AsynFullMaximizeSliceLengthJob;

    fn new_job<IP: MatrixPrecision, G: GlobalConfig>(
        #[comptime] ident: MatmulIdent,
        #[comptime] config: G,
    ) -> AsynFullMaximizeSliceLengthJob {
        let matrix_layout = config.matrix_layout(ident);

        let num_slices = match matrix_layout {
            MatrixLayout::RowMajor => config.tiling_scheme().elements_in_stage_row(ident),
            MatrixLayout::ColMajor => config.tiling_scheme().elements_in_stage_col(ident),
        };
        let unit_count = config.plane_dim() * config.num_loading_planes(ident);

        let num_tasks_per_unit = comptime!(div_ceil(num_slices, unit_count));

        AsynFullMaximizeSliceLengthJob {
            num_tasks_per_unit,
            unit_count,
            num_slices,
            ident,
        }
    }

    fn barrier_level() -> BarrierLevel {
        BarrierLevel::cube_manual(0u32)
    }
}

#[derive(CubeType, Clone, Copy)]
pub struct AsynFullMaximizeSliceLengthJob {
    #[cube(comptime)]
    num_tasks_per_unit: u32,
    #[cube(comptime)]
    unit_count: u32,
    #[cube(comptime)]
    num_slices: u32,
    #[cube(comptime)]
    ident: MatmulIdent,
}

#[cube]
impl<IP: MatrixPrecision> AsyncLoadingJob<IP, StridedTilingLayout>
    for AsynFullMaximizeSliceLengthJob
{
    fn execute_task<CM: CopyMechanism, G: GlobalConfig>(
        this: &mut Self,
        task_id: u32,
        tensor_reader: &GlobalIterator<Line<IP::Global>>,
        stage: &mut StridedStage<IP::Stage, StridedTilingLayout>,
        mechanism: &CM,
        #[comptime] config: G,
    ) {
        let nth_slice = this.unit_count * task_id + UNIT_POS;

        #[allow(clippy::collapsible_else_if)]
        if comptime!(this.num_slices.is_multiple_of(this.unit_count)) {
            load_nth_slice::<IP::Global, IP::Stage, CM, G>(
                nth_slice,
                tensor_reader,
                stage,
                mechanism,
                this.ident,
                config,
            );
        } else {
            if nth_slice < this.num_slices {
                load_nth_slice::<IP::Global, IP::Stage, CM, G>(
                    nth_slice,
                    tensor_reader,
                    stage,
                    mechanism,
                    this.ident,
                    config,
                );
            }
        };
    }

    fn task_count(this: &Self) -> comptime_type!(u32) {
        this.num_tasks_per_unit
    }
}

#[cube]
fn load_nth_slice<EG: Numeric, ES: Numeric, CM: CopyMechanism, G: GlobalConfig>(
    nth_slice: u32,
    global_iter: &GlobalIterator<Line<EG>>,
    stage: &mut StridedStage<ES, StridedTilingLayout>,
    mechanism: &CM,
    #[comptime] ident: MatmulIdent,
    #[comptime] config: G,
) {
    let window = load_window_in_stage(
        &global_iter.view(),
        nth_slice,
        comptime!(config.global_memory_config(ident)),
    );
    let mut destination: SliceMut<Line<ES>> = StridedTilingLayout::nth_slice::<ES>(
        stage,
        nth_slice,
        comptime!(config.stage_memory_config(ident)),
    );

    CM::memcpy_async(mechanism, &window.try_cast_unchecked(), &mut destination);
}