Skip to main content

cubek_convolution/kernels/forward/
launch.rs

1use crate::{
2    components::{ConvSetupError, ConvolutionOperation, ConvolutionProblem, Dimensionality},
3    forward::args::ConcreteArgs,
4    kernels::forward::selector::launch_kernel_concrete,
5    launch::ConvolutionArgs,
6    routines::Routine,
7};
8use cubecl::{client::Client, prelude::*};
9use cubek_matmul::{
10    definition::{AvailableVectorSizes, MatmulElems},
11    routine::BlueprintStrategy,
12};
13use cubek_std::{InputBinding, MatrixLayout};
14
15/// Forward-convolution dispatch helper.
16///
17/// Called by `cubek_convolution::launch_ref` after the routine and
18/// blueprint-strategy have been resolved. Not meant for direct external use.
19#[allow(clippy::result_large_err, clippy::too_many_arguments)]
20pub(crate) fn launch_internal<const N_SPATIAL: usize, Rt: Routine>(
21    client: &Client,
22    input: InputBinding,
23    weight: InputBinding,
24    bias: Option<InputBinding>,
25    out: TensorBinding,
26    args: ConvolutionArgs<N_SPATIAL>,
27    blueprint_strategy: &BlueprintStrategy<Rt::MatmulRoutine>,
28    dtypes: MatmulElems,
29) -> Result<(), ConvSetupError>
30where
31    Rt::Args: ConcreteArgs<Rt::MatmulRoutine>,
32{
33    let ConvolutionArgs {
34        stride,
35        padding,
36        dilation,
37    } = args;
38
39    let dimensionality = match N_SPATIAL {
40        1 => Dimensionality::Dim1,
41        2 => Dimensionality::Dim2,
42        3 => Dimensionality::Dim3,
43        other => unimplemented!("Unsupported dimensionality {other}"),
44    };
45
46    launch_with_routine::<Rt>(
47        client,
48        input,
49        weight,
50        bias,
51        out,
52        (&stride, &padding, &dilation),
53        dimensionality,
54        blueprint_strategy,
55        dtypes,
56    )
57}
58
59#[allow(clippy::too_many_arguments)]
60fn launch_with_routine<Rt: Routine>(
61    client: &Client,
62    input: InputBinding,
63    weight: InputBinding,
64    bias: Option<InputBinding>,
65    out: TensorBinding,
66    (stride, padding, dilation): (&[usize], &[usize], &[usize]),
67    dimensionality: Dimensionality,
68    blueprint_strategy: &BlueprintStrategy<Rt::MatmulRoutine>,
69    dtypes: MatmulElems,
70) -> Result<(), ConvSetupError>
71where
72    Rt::Args: ConcreteArgs<Rt::MatmulRoutine>,
73{
74    let rank = input.data().shape.len();
75    let dim_c = rank - 1;
76
77    let n = input.data().shape[0];
78    let c = input.data().shape[dim_c];
79
80    let out_c = weight.data().shape[0];
81
82    let in_shape = &input.data().shape[1..dim_c];
83    let kernel_shape = &weight.data().shape[1..dim_c];
84    let out_shape = &out.shape[1..dim_c];
85
86    let op = ConvolutionOperation::Forward;
87
88    let input_data = Rt::correct_layout(client, input.clone().into_data(), dtypes.lhs_global, op)?;
89    let weight_data =
90        Rt::correct_layout(client, weight.clone().into_data(), dtypes.rhs_global, op)?;
91
92    let mut input = input.clone();
93    let mut weight = weight.clone();
94
95    *input.data_mut() = input_data;
96    *weight.data_mut() = weight_data;
97
98    let address_type = input
99        .required_address_type()
100        .max(weight.required_address_type())
101        .max(
102            bias.clone()
103                .map(|bias| bias.required_address_type())
104                .unwrap_or_default(),
105        )
106        .max(out.required_address_type(dtypes.acc_global.size()));
107
108    let problem = ConvolutionProblem {
109        m: n * out_shape.iter().product::<usize>(),
110        n: out_c,
111        k: c * kernel_shape.iter().product::<usize>(),
112        lhs_strides: input.data().strides.clone(),
113        rhs_strides: weight.data().strides.clone(),
114        lhs_layout: MatrixLayout::RowMajor,
115        rhs_layout: MatrixLayout::ColMajor,
116        kernel_size: kernel_shape.iter().map(|it| *it as u32).collect(),
117        stride: stride.iter().map(|it| *it as u32).collect(),
118        padding: padding.iter().map(|it| *it as i32).collect(),
119        dilation: dilation.iter().map(|it| *it as u32).collect(),
120
121        batches: n,
122        in_shape: in_shape.into(),
123        out_shape: out_shape.into(),
124        channels: c,
125        out_channels: out_c,
126
127        padded_channels: c,
128        operation: op,
129
130        dimensionality,
131        global_dtypes: dtypes.as_global_elems(),
132        address_type,
133    };
134
135    launch_kernel::<Rt>(
136        client,
137        input,
138        weight,
139        bias,
140        out,
141        problem,
142        blueprint_strategy,
143        dtypes,
144    )
145}
146
147#[allow(clippy::result_large_err, clippy::too_many_arguments)]
148pub fn launch_kernel<Rt: Routine>(
149    client: &Client,
150    input: InputBinding,
151    weight: InputBinding,
152    bias: Option<InputBinding>,
153    out: TensorBinding,
154    problem: ConvolutionProblem,
155    blueprint_strategy: &BlueprintStrategy<Rt::MatmulRoutine>,
156    dtypes: MatmulElems,
157) -> Result<(), ConvSetupError>
158where
159    Rt::Args: ConcreteArgs<Rt::MatmulRoutine>,
160{
161    // Shape/strides are treated as k-major, with the last dim always being the contiguous one.
162    // So for the sake of selecting a vector size, the shape/strides are always row-major.
163    let mut vector_sizes = Rt::filter_vector_sizes(AvailableVectorSizes::from_type_sizes(
164        client,
165        input.data_elem_size(),
166        weight.data_elem_size(),
167        dtypes.acc_global.size(),
168    ))
169    .filter_lhs_with_tensor(
170        &input.data().strides,
171        &input.data().shape,
172        MatrixLayout::RowMajor,
173    )
174    .filter_rhs_with_tensor(
175        &weight.data().strides,
176        &weight.data().shape,
177        MatrixLayout::RowMajor,
178    )
179    .filter_out_with_tensor(&out.strides, &out.shape)
180    .pick_max()?;
181
182    // The large vector size resulting from dequantizing ends up slower due to restrictions on
183    // algorithms. Use this as a quick and dirty fix.
184    if input.scale().is_some() {
185        vector_sizes.lhs = 1;
186    }
187    if weight.scale().is_some() {
188        vector_sizes.rhs = 1;
189    }
190
191    launch_kernel_concrete::<Rt::Args, Rt::MatmulRoutine>(
192        client,
193        input,
194        weight,
195        bias,
196        out,
197        problem,
198        vector_sizes,
199        blueprint_strategy,
200        &dtypes,
201    )
202}