cubek_convolution/kernels/backward_data/
args.rs1use cubecl::{
2 Runtime,
3 client::ComputeClient,
4 prelude::*,
5 std::tensor::{
6 launch::ViewArg,
7 layout::{
8 VirtualLayoutLaunch,
9 chain::{Chain, ChainLaunch},
10 },
11 },
12 zspace::{shape, strides},
13};
14use cubek_matmul::{
15 args::*,
16 components::global::memory::{GlobalLayoutConfig, NoopLayout, NoopLayoutLaunch},
17 definition::{BatchMatmulBlueprint, Blueprint, MatmulElems},
18 routines::BatchMatmulRoutine,
19};
20use cubek_std::launch::tma::remap_storage_for_tma;
21use cubek_std::{InputBinding, MatrixLayout};
22use enumset::EnumSet;
23
24use crate::components::{
25 ConvolutionParams, ConvolutionProblem,
26 global::{
27 args::{RuntimeArgs, RuntimeArgsLaunch},
28 layout::{
29 Im2colLayout, Im2colLayoutLaunch, NhwcCheck, NhwcLayout, NhwcLayoutLaunch, OutLayout,
30 OutLayoutLaunch, TmaIm2colLayout, TmaIm2colLayoutLaunch, WeightLayout,
31 WeightLayoutLaunch,
32 },
33 },
34};
35
36pub trait ConcreteArgs<A: BatchMatmulRoutine<RuntimeArgs>>:
37 MatmulArgs<
38 Input<Vector<Lhs, LhsSize>, Vector<Rhs, RhsSize>, Vector<Acc, AccSize>>: ConcreteInputsFactory<A>,
39 Output<Vector<Acc, AccSize>>: ConcreteOutputFactory<A>,
40 Config = RuntimeArgs,
41 >
42{
43 fn adjust_problem<R: Runtime>(
44 client: &ComputeClient<R>,
45 problem: ConvolutionProblem,
46 selection: &A::Blueprint,
47 dtypes: &MatmulElems,
48 ) -> ConvolutionProblem;
49}
50
51impl<A: BatchMatmulRoutine<RuntimeArgs>> ConcreteArgs<A> for TensorArgs<RuntimeArgs> {
52 fn adjust_problem<R: Runtime>(
53 client: &ComputeClient<R>,
54 mut problem: ConvolutionProblem,
55 _blueprint: &A::Blueprint,
56 dtypes: &MatmulElems,
57 ) -> ConvolutionProblem {
58 let load_width = client.properties().hardware.load_width;
59 let channel_align = load_width as usize / dtypes.lhs_global.size_bits();
60 let padded_channels = problem.out_channels.next_multiple_of(channel_align);
61 let shape_k = problem.kernel_size.iter().product::<u32>() as usize * padded_channels;
62
63 problem.k = shape_k;
64 problem.padded_channels = padded_channels;
65
66 problem
67 }
68}
69
70impl<A: BatchMatmulRoutine<RuntimeArgs, Blueprint = BatchMatmulBlueprint>> ConcreteArgs<A>
71 for TensorMapArgs<RuntimeArgs>
72{
73 fn adjust_problem<R: Runtime>(
74 _client: &ComputeClient<R>,
75 mut problem: ConvolutionProblem,
76 blueprint: &BatchMatmulBlueprint,
77 _dtypes: &MatmulElems,
78 ) -> ConvolutionProblem {
79 let channel_align = blueprint.tiling_scheme.tile_size.k() as usize;
80 let padded_channels = problem.out_channels.next_multiple_of(channel_align);
81 let shape_k = problem.kernel_size.iter().product::<u32>() as usize * padded_channels;
82
83 problem.k = shape_k;
84 problem.padded_channels = padded_channels;
85
86 problem
87 }
88}
89
90pub trait ConcreteInputsFactory<A: BatchMatmulRoutine<RuntimeArgs>>: LaunchArg {
93 #[allow(clippy::too_many_arguments)]
94 fn create<R: Runtime>(
95 out_grad: InputBinding<R>,
96 weights: InputBinding<R>,
97 blueprint: &A::Blueprint,
98 problem: &ConvolutionProblem,
99 dtypes: &MatmulElems,
100 ) -> (Self::RuntimeArg<R>, RuntimeArgsLaunch<R>);
101}
102
103pub trait ConcreteOutputFactory<A: BatchMatmulRoutine<RuntimeArgs>>: LaunchArg {
106 fn create<R: Runtime>(
107 out: TensorBinding<R>,
108 blueprint: &A::Blueprint,
109 problem: &ConvolutionProblem,
110 ) -> Self::RuntimeArg<R>;
111}
112
113impl<Lhs: CubePrimitive, Rhs: CubePrimitive, EO: CubePrimitive, A: BatchMatmulRoutine<RuntimeArgs>>
114 ConcreteInputsFactory<A> for TensorInputs<Lhs, Rhs, EO>
115{
116 fn create<R: Runtime>(
117 out_grad: InputBinding<R>,
118 weights: InputBinding<R>,
119 blueprint: &A::Blueprint,
120 problem: &ConvolutionProblem,
121 _dtypes: &MatmulElems,
122 ) -> (Self::RuntimeArg<R>, RuntimeArgsLaunch<R>) {
123 type LhsLayout = Chain<NhwcLayout, Im2colLayout>;
124 type RhsLayout = Chain<NhwcLayout, WeightLayout>;
125
126 let padded_channels = problem.padded_channels as u32;
127 let params = ConvolutionParams::from_problem(problem);
128
129 let layout_lhs =
130 Im2colLayoutLaunch::from_args(problem, params, blueprint.lhs_global_layout_config());
131 let layout_rhs =
132 WeightLayoutLaunch::from_args(problem, blueprint.rhs_global_layout_config());
133
134 let layout_lhs = {
135 let mut checks = EnumSet::empty();
136 if problem.should_check_spatial_bounds() {
137 checks.insert(NhwcCheck::Spatial);
138 }
139 if problem.should_check_channel() {
140 checks.insert(NhwcCheck::Channel);
141 }
142 let global = NhwcLayoutLaunch::checked(checks);
143 ChainLaunch::new(global, layout_lhs)
144 };
145 let layout_rhs = {
146 let mut checks = EnumSet::empty();
147 if problem.should_check_channel() {
148 checks.insert(NhwcCheck::Batch);
149 }
150 let global = NhwcLayoutLaunch::checked(checks);
151 ChainLaunch::new(global, layout_rhs)
152 };
153
154 let inputs = TensorInputsLaunch::new(
155 VirtualLayoutLaunch::new::<NoopLayout>(NoopLayoutLaunch::new()),
156 ViewArg::new_tensor::<LhsLayout>(out_grad.into_data().into_tensor_arg(), layout_lhs),
157 VirtualLayoutLaunch::new::<NoopLayout>(NoopLayoutLaunch::new()),
158 ViewArg::new_tensor::<RhsLayout>(weights.into_data().into_tensor_arg(), layout_rhs),
159 ComptimeOptionArgs::None,
160 ComptimeOptionArgs::None,
161 );
162
163 let runtime_args = RuntimeArgsLaunch::new(
164 problem.k as u32,
165 problem.out_channels as u32,
166 padded_channels,
167 problem.operation,
168 );
169
170 (inputs, runtime_args)
171 }
172}
173
174impl<EG: CubePrimitive, A: BatchMatmulRoutine<RuntimeArgs>> ConcreteOutputFactory<A>
175 for TensorOutput<EG>
176{
177 fn create<R: Runtime>(
178 out: TensorBinding<R>,
179 blueprint: &A::Blueprint,
180 problem: &ConvolutionProblem,
181 ) -> Self::RuntimeArg<R> {
182 type Layout = Chain<NhwcLayout, OutLayout>;
183
184 let global = NhwcLayoutLaunch::unchecked();
185 let layout = OutLayoutLaunch::from_args(problem, blueprint.out_global_layout_config());
186 let layout = ChainLaunch::new(global, layout);
187 let view = ViewArg::new_tensor::<Layout>(out.into_tensor_arg(), layout);
188 let batch = VirtualLayoutLaunch::new::<NoopLayout>(NoopLayoutLaunch::new());
189 TensorOutputLaunch::new(view, batch)
190 }
191}
192
193impl<
194 Lhs: CubePrimitive,
195 Rhs: CubePrimitive,
196 EO: CubePrimitive,
197 A: BatchMatmulRoutine<RuntimeArgs, Blueprint = BatchMatmulBlueprint>,
198> ConcreteInputsFactory<A> for TensorMapInputs<Lhs, Rhs, EO>
199{
200 fn create<R: Runtime>(
201 out_grad: InputBinding<R>,
202 weights: InputBinding<R>,
203 blueprint: &BatchMatmulBlueprint,
204 problem: &ConvolutionProblem,
205 dtypes: &MatmulElems,
206 ) -> (Self::RuntimeArg<R>, RuntimeArgsLaunch<R>) {
207 type LhsLayout = TmaIm2colLayout;
208 type RhsLayout = WeightLayout;
209
210 let tiling_scheme = blueprint.tiling_scheme;
211 let stage_m = tiling_scheme.elements_per_stage_along_m();
212 let stage_n = tiling_scheme.elements_per_stage_along_n();
213 let stage_k = tiling_scheme.elements_per_stage_along_k();
214 let tile_size_k = tiling_scheme.tile_size.k;
215
216 let mut stage_size_rhs = shape![1; problem.dimensionality.num_dims()];
217 stage_size_rhs.insert(0, stage_k as usize);
218 stage_size_rhs.push(stage_n as usize);
219
220 let lhs_elem = remap_storage_for_tma(dtypes.lhs_stage);
221
222 let mut elem_stride = strides![1; 2 + problem.stride.len()];
223
224 for (i, stride) in problem.stride.iter().enumerate() {
225 elem_stride[i + 1] = *stride as usize;
226 }
227
228 let lhs = TensorMapArg::new(
229 Im2colArgs {
230 pixel_box_lower_corner: calculate_lower_corner(problem),
231 pixel_box_upper_corner: calculate_upper_corner(problem),
232 channels_per_pixel: tile_size_k,
233 pixels_per_column: stage_m,
234 },
235 out_grad.into_data().into_tensor_arg(),
236 lhs_elem,
237 )
238 .with_elem_stride(elem_stride);
239
240 let rhs = TensorMapArg::new(
241 TiledArgs {
242 tile_size: stage_size_rhs,
243 },
244 weights.into_data().into_tensor_arg(),
245 dtypes.rhs_global,
246 );
247
248 let padded_channels = problem.padded_channels as u32;
249 let shape_k = problem.k as u32;
250
251 let stages_lhs = A::num_stages().lhs;
255 let stages_size_k = blueprint.tiling_scheme.elements_per_stage_along_k() * stages_lhs;
256 let check_kernel = !shape_k.is_multiple_of(stages_size_k);
257 let lhs_layout = TmaIm2colLayoutLaunch::from_args(problem, check_kernel);
258 let rhs_layout = WeightLayoutLaunch::from_args(
259 problem,
260 GlobalLayoutConfig {
261 check_row_bounds: false,
262 check_col_bounds: false,
263 matrix_layout: MatrixLayout::default(),
264 },
265 );
266
267 let inputs = TensorMapInputsLaunch::new(
268 ViewArg::new_tensor_map_im2col::<LhsLayout, _, _>(lhs, lhs_layout),
269 ViewArg::new_tensor_map_tiled::<RhsLayout>(rhs, rhs_layout),
270 ComptimeOptionArgs::None,
271 ComptimeOptionArgs::None,
272 );
273
274 let runtime_args = RuntimeArgsLaunch::new(
275 shape_k,
276 problem.out_channels as u32,
277 padded_channels,
278 problem.operation,
279 );
280
281 (inputs, runtime_args)
282 }
283}
284
285#[allow(clippy::needless_range_loop)]
286fn calculate_lower_corner(problem: &ConvolutionProblem) -> Vec<i32> {
287 let mut out = vec![0; problem.padding.len()];
288 for i in 0..problem.padding.len() {
289 out[i] =
290 problem.padding[i] - (problem.kernel_size[i] as i32 - 1) * problem.dilation[i] as i32;
291 }
292 out
293}
294
295#[allow(clippy::needless_range_loop)]
296fn calculate_upper_corner(problem: &ConvolutionProblem) -> Vec<i32> {
297 let mut out = vec![0; problem.padding.len()];
298 for i in 0..problem.padding.len() {
299 out[i] = problem.padding[i]
300 - (problem.kernel_size[i] as i32 - 1) * problem.dilation[i] as i32
301 + problem.in_shape[i] as i32
302 - problem.out_shape[i] as i32;
303 }
304 out
305}