Skip to main content

rudnn/convolution/kernels/backward_data/
args.rs

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