Skip to main content

rudnn/convolution/kernels/forward/
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, stage::SwizzleMode};
19use enumset::EnumSet;
20
21use crate::convolution::components::{
22    ConvolutionParams, ConvolutionProblem,
23    global::{
24        args::{RuntimeArgs, RuntimeArgsLaunch},
25        layout::{
26            BiasLayout, Im2colLayout, Im2colLayoutLaunch, NhwcCheck, NhwcLayout, NhwcLayoutLaunch,
27            OutLayout, 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        blueprint: &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.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 = match blueprint.swizzle_modes.lhs {
77            SwizzleMode::None => blueprint.tiling_scheme.tile_size.k() as usize,
78            _ => blueprint.tiling_scheme.elements_per_stage_along_k() as usize,
79        };
80        let padded_channels = problem.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
90/// Create the input runtime arguments for a matmul kernel that works on concrete inputs and
91/// output (not fused).
92pub trait ConcreteInputsFactory<A: Routine<RuntimeArgs>>: LaunchArg {
93    #[allow(clippy::too_many_arguments)]
94    fn create<R: Runtime>(
95        lhs: InputBinding<R>,
96        rhs: InputBinding<R>,
97        bias: Option<InputBinding<R>>,
98        blueprint: &A::Blueprint,
99        problem: &ConvolutionProblem,
100        dtypes: &MatmulElems,
101    ) -> (Self::RuntimeArg<R>, RuntimeArgsLaunch<R>);
102}
103
104/// Create the output runtime arguments for a matmul kernel that works on concrete inputs and
105/// output (not fused).
106pub trait ConcreteOutputFactory<A: Routine<RuntimeArgs>>: LaunchArg {
107    fn create<R: Runtime>(
108        out: TensorBinding<R>,
109        blueprint: &A::Blueprint,
110        problem: &ConvolutionProblem,
111        dtypes: &MatmulElems,
112    ) -> Self::RuntimeArg<R>;
113}
114
115impl<Lhs: RudaPrimitive, Rhs: RudaPrimitive, EO: RudaPrimitive, A: Routine<RuntimeArgs>>
116    ConcreteInputsFactory<A> for TensorInputs<Lhs, Rhs, EO>
117{
118    fn create<R: Runtime>(
119        lhs: InputBinding<R>,
120        rhs: InputBinding<R>,
121        bias: Option<InputBinding<R>>,
122        blueprint: &A::Blueprint,
123        problem: &ConvolutionProblem,
124        _dtypes: &MatmulElems,
125    ) -> (Self::RuntimeArg<R>, RuntimeArgsLaunch<R>) {
126        type LhsLayout = Chain<NhwcLayout, Im2colLayout>;
127        type RhsLayout = Chain<NhwcLayout, WeightLayout>;
128
129        let padded_channels = problem.padded_channels as u32;
130        let conv_params = ConvolutionParams::from_problem(problem);
131
132        let layout_lhs = Im2colLayoutLaunch::from_args(
133            problem,
134            conv_params,
135            blueprint.lhs_global_layout_config(),
136        );
137        let layout_rhs =
138            WeightLayoutLaunch::from_args(problem, blueprint.rhs_global_layout_config());
139
140        let layout_lhs = {
141            let mut checks = EnumSet::empty();
142            if problem.should_check_spatial_bounds() {
143                checks.insert(NhwcCheck::Spatial);
144            }
145            if problem.should_check_channel() {
146                checks.insert(NhwcCheck::Channel);
147            }
148            let global = NhwcLayoutLaunch::checked(checks);
149            ChainLaunch::new(global, layout_lhs)
150        };
151        let layout_rhs = {
152            let mut checks = EnumSet::empty();
153            if problem.should_check_channel() {
154                checks.insert(NhwcCheck::Channel);
155            }
156            let global = NhwcLayoutLaunch::checked(checks);
157            ChainLaunch::new(global, layout_rhs)
158        };
159
160        let inputs = TensorInputsLaunch::new(
161            VirtualLayoutLaunch::new::<NoopLayout>(NoopLayoutLaunch::new()),
162            ViewArg::new_tensor::<LhsLayout>(lhs.into_data().into_tensor_arg(), layout_lhs),
163            VirtualLayoutLaunch::new::<NoopLayout>(NoopLayoutLaunch::new()),
164            ViewArg::new_tensor::<RhsLayout>(rhs.into_data().into_tensor_arg(), layout_rhs),
165            bias.as_ref()
166                .map(|_| VirtualLayoutLaunch::new::<NoopLayout>(NoopLayoutLaunch::new()))
167                .into(),
168            bias.map(|bias| {
169                ViewArg::new_tensor::<BiasLayout>(bias.into_data().into_tensor_arg(), ())
170            })
171            .into(),
172        );
173
174        let runtime_args = RuntimeArgsLaunch::new(
175            problem.k as u32,
176            problem.channels as u32,
177            padded_channels,
178            conv_params.operation,
179        );
180
181        (inputs, runtime_args)
182    }
183}
184
185impl<EG: RudaPrimitive, A: Routine<RuntimeArgs>> ConcreteOutputFactory<A> for TensorOutput<EG> {
186    fn create<R: Runtime>(
187        out: TensorBinding<R>,
188        blueprint: &A::Blueprint,
189        problem: &ConvolutionProblem,
190        _dtypes: &MatmulElems,
191    ) -> Self::RuntimeArg<R> {
192        type Layout = Chain<NhwcLayout, OutLayout>;
193
194        let global = NhwcLayoutLaunch::unchecked();
195        let layout = OutLayoutLaunch::from_args(problem, blueprint.out_global_layout_config());
196        let layout = ChainLaunch::new(global, layout);
197        let view = ViewArg::new_tensor::<Layout>(out.into_tensor_arg(), layout);
198        let batch = VirtualLayoutLaunch::new::<NoopLayout>(NoopLayoutLaunch::new());
199        TensorOutputLaunch::new(view, batch)
200    }
201}
202
203impl<
204    Lhs: RudaPrimitive,
205    Rhs: RudaPrimitive,
206    EO: RudaPrimitive,
207    A: Routine<RuntimeArgs, Blueprint = TilingBlueprint>,
208> ConcreteInputsFactory<A> for TensorMapInputs<Lhs, Rhs, EO>
209{
210    fn create<R: Runtime>(
211        lhs: InputBinding<R>,
212        rhs: InputBinding<R>,
213        bias: Option<InputBinding<R>>,
214        blueprint: &TilingBlueprint,
215        problem: &ConvolutionProblem,
216        dtypes: &MatmulElems,
217    ) -> (Self::RuntimeArg<R>, RuntimeArgsLaunch<R>) {
218        let tiling_scheme = blueprint.tiling_scheme;
219        let stage_m = tiling_scheme.elements_per_stage_along_m();
220        let stage_n = tiling_scheme.elements_per_stage_along_n();
221
222        let tile_size_k = match blueprint.swizzle_modes.lhs {
223            SwizzleMode::None => tiling_scheme.tile_size.k,
224            _ => tiling_scheme.elements_per_stage_along_k(),
225        };
226
227        let mut stage_size_rhs = shape![1; problem.dimensionality.num_dims()];
228        stage_size_rhs.insert(0, stage_n as usize);
229        stage_size_rhs.push(tile_size_k as usize);
230
231        let lhs_elem = remap_storage_for_tma(dtypes.lhs_stage);
232
233        let mut elem_stride = strides![1; 2 + problem.stride.len()];
234
235        for (i, stride) in problem.stride.iter().enumerate() {
236            elem_stride[i + 1] = *stride as usize;
237        }
238
239        let lhs = TensorMapArg::new(
240            Im2colArgs {
241                pixel_box_lower_corner: calculate_lower_corner(&problem.padding),
242                pixel_box_upper_corner: calculate_upper_corner(
243                    &problem.padding,
244                    &problem.kernel_size,
245                    &problem.dilation,
246                ),
247                channels_per_pixel: tile_size_k,
248                pixels_per_column: stage_m,
249            },
250            lhs.clone().into_data().into_tensor_arg(),
251            lhs_elem,
252        )
253        .with_elem_stride(elem_stride)
254        .with_swizzle(blueprint.swizzle_modes.lhs.into());
255
256        let rhs = TensorMapArg::new(
257            TiledArgs {
258                tile_size: stage_size_rhs,
259            },
260            rhs.clone().into_data().into_tensor_arg(),
261            dtypes.rhs_global,
262        )
263        .with_swizzle(blueprint.swizzle_modes.rhs.into());
264
265        let padded_channels = problem.padded_channels as u32;
266        let shape_k = problem.k as u32;
267
268        // Im2col needs extra checking because if `k` is OOB it wraps around the kernel and can load
269        // in-bounds but not in-kernel elements. Other TMA layouts are always outside the shape if
270        // any matrix dim is out of bounds.
271        let stages_lhs = A::num_stages().lhs;
272        let stages_size_k = blueprint.tiling_scheme.elements_per_stage_along_k() * stages_lhs;
273        let check_kernel = !shape_k.is_multiple_of(stages_size_k);
274        let lhs_layout = TmaIm2colLayoutLaunch::from_args(problem, check_kernel);
275        let rhs_layout = WeightLayoutLaunch::from_args(
276            problem,
277            GlobalLayoutConfig {
278                check_row_bounds: false,
279                check_col_bounds: false,
280                matrix_layout: MatrixLayout::ColMajor,
281            },
282        );
283
284        let bias = bias
285            .map(|bias| ViewArg::new_tensor::<BiasLayout>(bias.into_data().into_tensor_arg(), ()));
286
287        let inputs = TensorMapInputsLaunch::new(
288            ViewArg::new_tensor_map_im2col::<TmaIm2colLayout, _, _>(lhs, lhs_layout),
289            ViewArg::new_tensor_map_tiled::<WeightLayout>(rhs, rhs_layout),
290            bias.into(),
291            ComptimeOptionArgs::Some(VirtualLayoutLaunch::new::<NoopLayout>(
292                NoopLayoutLaunch::new(),
293            )),
294        );
295
296        let runtime_args = RuntimeArgsLaunch::new(
297            shape_k,
298            problem.channels as u32,
299            padded_channels,
300            problem.operation,
301        );
302
303        (inputs, runtime_args)
304    }
305}
306
307fn calculate_lower_corner(padding: &[i32]) -> Vec<i32> {
308    padding.iter().map(|padding| -*padding).collect()
309}
310
311fn calculate_upper_corner(padding: &[i32], kernel_size: &[u32], dilation: &[u32]) -> Vec<i32> {
312    padding
313        .iter()
314        .zip(kernel_size)
315        .zip(dilation)
316        .map(|((padding, kernel_size), dilation)| {
317            *padding - (*kernel_size - 1) as i32 * *dilation as i32
318        })
319        .collect()
320}