Skip to main content

cubek_convolution/kernels/forward/
launch.rs

1use crate::{
2    AcceleratedTileKind, ReadingStrategy, algorithm::simple::*,
3    components::global::args::RuntimeArgs,
4};
5use crate::{
6    ConvolutionArgs, Strategy, components::ConvolutionOperation, forward::args::ConcreteArgs,
7};
8use crate::{
9    algorithm::Algorithm,
10    components::{ConvolutionProblem, Dimensionality},
11};
12use crate::{components::ConvSetupError, kernels::forward::selector::launch_kernel_concrete};
13use cubecl::{Runtime, client::ComputeClient, prelude::*, std::tensor::TensorHandle};
14use cubek_matmul::{
15    components::tile::{cmma::CmmaMatmul, io::Strided, mma::MmaMatmul},
16    definition::{AvailableLineSizes, MatmulElems, MatrixLayout},
17};
18use cubek_matmul::{definition, launch::MatmulInputHandleRef};
19use cubek_matmul::{launch::MatmulInputHandle, routines::BlueprintStrategy};
20use derive_new::new;
21
22macro_rules! with_tile_kind {
23    ($kind: expr, $T: ident, $launch: expr) => {
24        match $kind {
25            AcceleratedTileKind::Cmma => {
26                type $T = CmmaMatmul<Option<Strided>>;
27                ($launch)()
28            }
29            AcceleratedTileKind::Mma => {
30                type $T = MmaMatmul<Strided, Strided, Option<Strided>>;
31                ($launch)()
32            }
33        }
34    };
35}
36
37#[allow(clippy::result_large_err, clippy::too_many_arguments)]
38pub fn launch<R: Runtime, const N_SPATIAL: usize>(
39    strategy: &Strategy,
40    client: &ComputeClient<R>,
41    input: MatmulInputHandle<R>,
42    weight: MatmulInputHandle<R>,
43    bias: Option<MatmulInputHandle<R>>,
44    out: TensorHandle<R>,
45    args: ConvolutionArgs<N_SPATIAL>,
46    dtypes: MatmulElems,
47) -> Result<(), ConvSetupError> {
48    launch_ref(
49        strategy,
50        client,
51        &input.as_ref(),
52        &weight.as_ref(),
53        &bias.as_ref().map(|it| it.as_ref()),
54        &out.as_ref(),
55        args,
56        dtypes,
57    )
58}
59
60/// Perform an n-dimensional convolution using the implicit GEMM (im2col) algorithm, using cubecl
61/// tiling matmul components, using the specified algorithm.
62///
63/// * `input` - The input feature map, layout should be [batches, depth, height, width, in_channels]
64/// * `weight` - The weights (filter) applied to each kernel, layout should be [out_channels, kernel_d, kernel_h, kernel_w, in_channels]
65/// * `out` - The output feature map, layout should be [batches, out_depth, out_height, out_width, out_channels]
66/// * `bias` - The bias added to each out channel
67/// * `options` - The options to use for the convolution
68#[allow(clippy::result_large_err, clippy::too_many_arguments)]
69pub fn launch_ref<R: Runtime, const N_SPATIAL: usize>(
70    strategy: &Strategy,
71    client: &ComputeClient<R>,
72    input: &MatmulInputHandleRef<'_, R>,
73    weight: &MatmulInputHandleRef<'_, R>,
74    bias: &Option<MatmulInputHandleRef<'_, R>>,
75    out: &TensorHandleRef<'_, R>,
76    args: ConvolutionArgs<N_SPATIAL>,
77    dtypes: MatmulElems,
78) -> Result<(), ConvSetupError> {
79    let conv = Convolution::new(client, input, weight, bias, out, args, dtypes);
80
81    match strategy {
82        Strategy::Simple {
83            read_strategy,
84            tile_kind,
85        } => with_tile_kind!(tile_kind, Accelerated, || match read_strategy {
86            ReadingStrategy::Cyclic => conv.launch::<SimpleSyncCyclicConv<Accelerated>>(),
87            ReadingStrategy::Strided => conv.launch::<SimpleSyncStridedConv<Accelerated>>(),
88            ReadingStrategy::Tilewise => conv.launch::<SimpleSyncTilewiseConv<Accelerated>>(),
89            ReadingStrategy::AsyncCyclic => conv.launch::<SimpleAsyncCyclicConv<Accelerated>>(),
90            ReadingStrategy::AsyncStrided => conv.launch::<SimpleAsyncStridedConv<Accelerated>>(),
91            ReadingStrategy::Tma => conv.launch::<SimpleAsyncTmaConv<Accelerated>>(),
92        }),
93    }
94}
95
96#[derive(new)]
97struct Convolution<'a, R: Runtime, const N_SPATIAL: usize> {
98    client: &'a ComputeClient<R>,
99    input: &'a MatmulInputHandleRef<'a, R>,
100    weight: &'a MatmulInputHandleRef<'a, R>,
101    bias: &'a Option<MatmulInputHandleRef<'a, R>>,
102    out: &'a TensorHandleRef<'a, R>,
103    args: ConvolutionArgs<N_SPATIAL>,
104    dtypes: MatmulElems,
105}
106
107impl<'a, R: Runtime, const N_SPATIAL: usize> Convolution<'a, R, N_SPATIAL> {
108    fn launch<Alg: Algorithm>(self) -> Result<(), ConvSetupError>
109    where
110        Alg::Args: ConcreteArgs<Alg::Routine>,
111    {
112        let ConvolutionArgs {
113            stride,
114            padding,
115            dilation,
116        } = self.args;
117
118        let dimensionality = match N_SPATIAL {
119            1 => Dimensionality::Dim1,
120            2 => Dimensionality::Dim2,
121            3 => Dimensionality::Dim3,
122            other => unimplemented!("Unsupported dimensionality {other}"),
123        };
124
125        launch_with_algorithm::<R, Alg>(
126            self.client,
127            self.input,
128            self.weight,
129            self.bias,
130            self.out,
131            (&stride, &padding, &dilation),
132            dimensionality,
133            &BlueprintStrategy::Inferred(Default::default()),
134            self.dtypes,
135        )
136    }
137}
138
139#[allow(clippy::too_many_arguments)]
140fn launch_with_algorithm<R: Runtime, Alg: Algorithm>(
141    client: &ComputeClient<R>,
142    input: &MatmulInputHandleRef<'_, R>,
143    weight: &MatmulInputHandleRef<'_, R>,
144    bias: &Option<MatmulInputHandleRef<'_, R>>,
145    out: &TensorHandleRef<'_, R>,
146    (stride, padding, dilation): (&[usize], &[usize], &[usize]),
147    dimensionality: Dimensionality,
148    blueprint_strategy: &BlueprintStrategy<RuntimeArgs, Alg::Routine>,
149    dtypes: MatmulElems,
150) -> Result<(), ConvSetupError>
151where
152    Alg::Args: ConcreteArgs<Alg::Routine>,
153{
154    let rank = input.data().shape.len();
155    let dim_c = rank - 1;
156
157    let n = input.data().shape[0];
158    let c = input.data().shape[dim_c];
159
160    let out_c = weight.data().shape[0];
161
162    let in_shape = &input.data().shape[1..dim_c];
163    let kernel_shape = &weight.data().shape[1..dim_c];
164    let out_shape = &out.shape[1..dim_c];
165
166    let op = ConvolutionOperation::Forward;
167
168    let input_data = Alg::into_tensor_handle(client, input.data(), dtypes.lhs_global, op)?;
169    let weight_data = Alg::into_tensor_handle(client, weight.data(), dtypes.rhs_global, op)?;
170
171    let mut input = *input;
172    let mut weight = *weight;
173
174    *input.data_mut() = input_data.as_ref();
175    *weight.data_mut() = weight_data.as_ref();
176
177    let address_type = input
178        .required_address_type()
179        .max(weight.required_address_type())
180        .max(
181            bias.map(|bias| bias.required_address_type())
182                .unwrap_or_default(),
183        )
184        .max(out.required_address_type());
185
186    let problem = ConvolutionProblem {
187        m: n * out_shape.iter().product::<usize>(),
188        n: out_c,
189        k: c * kernel_shape.iter().product::<usize>(),
190        lhs_strides: input.data().strides.into(),
191        rhs_strides: weight.data().strides.into(),
192        lhs_layout: definition::MatrixLayout::RowMajor,
193        rhs_layout: definition::MatrixLayout::ColMajor,
194        kernel_size: kernel_shape.iter().map(|it| *it as u32).collect(),
195        stride: stride.iter().map(|it| *it as u32).collect(),
196        padding: padding.iter().map(|it| *it as i32).collect(),
197        dilation: dilation.iter().map(|it| *it as u32).collect(),
198
199        batches: n,
200        in_shape: in_shape.into(),
201        out_shape: out_shape.into(),
202        channels: c,
203        out_channels: out_c,
204
205        padded_channels: c,
206        operation: op,
207
208        dimensionality,
209        global_dtypes: dtypes.as_global_elems(),
210        address_type,
211    };
212
213    launch_kernel::<R, Alg>(
214        client,
215        &input,
216        &weight,
217        bias,
218        out,
219        problem,
220        blueprint_strategy,
221        dtypes,
222    )
223}
224
225#[allow(clippy::result_large_err, clippy::too_many_arguments)]
226pub fn launch_kernel<R: Runtime, Alg: Algorithm>(
227    client: &ComputeClient<R>,
228    input: &MatmulInputHandleRef<'_, R>,
229    weight: &MatmulInputHandleRef<'_, R>,
230    bias: &Option<MatmulInputHandleRef<'_, R>>,
231    out: &TensorHandleRef<'_, R>,
232    problem: ConvolutionProblem,
233    blueprint_strategy: &BlueprintStrategy<RuntimeArgs, Alg::Routine>,
234    dtypes: MatmulElems,
235) -> Result<(), ConvSetupError>
236where
237    Alg::Args: ConcreteArgs<Alg::Routine>,
238{
239    // Shape/strides are treated as k-major, with the last dim always being the contiguous one.
240    // So for the sake of selecting a line size, the shape/strides are always row-major.
241    let line_sizes = AvailableLineSizes::from_type_sizes(
242        client,
243        input.data().elem_size,
244        weight.data().elem_size,
245        out.elem_size,
246    )
247    .filter_lhs_with_tensor(
248        input.data().strides,
249        input.data().shape,
250        MatrixLayout::RowMajor,
251    )
252    .filter_rhs_with_tensor(
253        weight.data().strides,
254        weight.data().shape,
255        MatrixLayout::RowMajor,
256    )
257    .filter_out_with_tensor(out.strides, out.shape);
258
259    let mut line_sizes = Alg::filter_line_sizes(line_sizes).pick_max()?;
260
261    // The large line size resulting from dequantizing ends up slower due to restrictions on
262    // algorithms. Use this as a quick and dirty fix.
263    if input.scale().is_some() {
264        line_sizes.lhs = 1;
265    }
266    if weight.scale().is_some() {
267        line_sizes.rhs = 1;
268    }
269
270    launch_kernel_concrete::<R, Alg::Args, Alg::Routine>(
271        client,
272        input,
273        weight,
274        bias,
275        out,
276        problem,
277        line_sizes,
278        blueprint_strategy,
279        &dtypes,
280    )
281}