1use crate::{
2 ReduceError, ReducePrecision, VectorizationMode,
3 components::{
4 args::{NumericVector, ReduceArgs, TensorArgs, init_tensors},
5 global::{
6 cube::GlobalFullCubeReduce, plane::GlobalFullPlaneReduce, unit::GlobalFullUnitReduce,
7 },
8 instructions::*,
9 },
10 launch::{ReduceStrategy, RoutineStrategy, generate_vector_size},
11 output_vectorization_axis,
12 routines::{
13 GlobalReduceBlueprint, ReduceBlueprint, ReduceLaunchSettings, ReduceProblem,
14 ReduceVectorSettings, Routine, cube::CubeRoutine, plane::PlaneRoutine, unit::UnitRoutine,
15 },
16};
17use cubecl::{prelude::*, std::tensor::r#virtual::VirtualTensor};
18
19const TOPK_VECTOR_SLOTS: usize = 32;
23
24#[derive(Clone, Copy, Debug)]
25pub struct ReduceDtypes {
26 pub input: ElemType,
27 pub output: ElemType,
28 pub accumulation: ElemType,
29}
30
31#[derive(Clone, Copy, Debug)]
33pub struct ReduceWithIndicesDtypes {
34 pub input: ElemType,
35 pub values: ElemType,
37 pub indices: ElemType,
39 pub accumulation: ElemType,
40}
41
42impl ReduceWithIndicesDtypes {
43 fn values_dtypes(&self) -> ReduceDtypes {
44 ReduceDtypes {
45 input: self.input,
46 output: self.values,
47 accumulation: self.accumulation,
48 }
49 }
50}
51
52#[allow(clippy::too_many_arguments)]
63fn prepare_reduce_launch(
64 client: &Client,
65 input: &TensorBinding,
66 output: &TensorBinding,
67 reduce_axis: usize,
68 strategy: ReduceStrategy,
69 dtypes: ReduceDtypes,
70 inst: ReduceOperationConfig,
71 address_type: AddressType,
72 second_output: Option<ElemType>,
73) -> Result<(ReduceBlueprint, ReduceLaunchSettings, usize), ReduceError> {
74 let reduce_len = input.shape[reduce_axis];
79 let reduce_count: usize = input
80 .shape
81 .iter()
82 .enumerate()
83 .filter(|(axis, _)| *axis != reduce_axis)
84 .map(|(_, len)| len)
85 .product();
86
87 let problem = ReduceProblem {
88 reduce_len,
89 reduce_count,
90 axis: reduce_axis,
91 dtypes,
92 instruction: inst,
93 address_type,
94 };
95 let vectorization_mode = match input.strides[reduce_axis] {
96 1 => VectorizationMode::Parallel,
97 _ => VectorizationMode::Perpendicular,
98 };
99
100 let out_vec_axis = output_vectorization_axis(&input.strides, reduce_axis, vectorization_mode);
101
102 let (vector_size_input, vector_size_output) = generate_vector_size(
103 client,
104 input,
105 output,
106 reduce_axis,
107 problem.dtypes.input,
108 vectorization_mode,
109 &strategy.vectorization,
110 );
111 let (vector_size_input, vector_size_output) = match &problem.instruction {
131 ReduceOperationConfig::TopK(k) | ReduceOperationConfig::ArgTopK(k)
132 if *k * vector_size_input > TOPK_VECTOR_SLOTS
133 || *k * *k > crate::components::instructions::TOPK_UNROLL_BUDGET =>
134 {
135 (1, 1)
136 }
137 _ => (vector_size_input, vector_size_output),
138 };
139
140 let vector_size_output = match second_output {
146 None => vector_size_output,
147 Some(index_dtype) => client
148 .io_optimized_vector_sizes(index_dtype.size())
149 .filter(|&width| width <= vector_size_output)
150 .max()
151 .unwrap_or(1),
152 };
153 let settings = ReduceVectorSettings {
154 vectorization_mode,
155 vector_size_input,
156 vector_size_output,
157 unchecked_fast_paths: matches!(
158 strategy.autotune_level,
159 cubecl::config::autotune::AutotuneLevel::Full
160 ),
161 fuse_on_read: false,
162 };
163
164 let (blueprint, settings) = match strategy.routine {
165 RoutineStrategy::Unit(strategy) => {
166 let routine = UnitRoutine;
167 routine.prepare(client, problem, settings, strategy)?
168 }
169 RoutineStrategy::Plane(strategy) => {
170 let routine = PlaneRoutine;
171 routine.prepare(client, problem, settings, strategy)?
172 }
173 RoutineStrategy::Cube(strategy) => {
174 let routine = CubeRoutine;
175 routine.prepare(client, problem, settings, strategy)?
176 }
177 };
178
179 Ok((blueprint, settings, out_vec_axis))
180}
181
182#[allow(clippy::too_many_arguments)]
186pub(crate) fn launch_reduce(
187 client: &Client,
188 input: TensorBinding,
189 output: TensorBinding,
190 reduce_axis: usize,
191 strategy: ReduceStrategy,
192 dtypes: ReduceDtypes,
193 inst: ReduceOperationConfig,
194) -> Result<(), ReduceError> {
195 let address_type = input
196 .required_address_type(dtypes.input.size())
197 .max(output.required_address_type(dtypes.output.size()));
198
199 let (blueprint, settings, out_vec_axis) = prepare_reduce_launch(
200 client,
201 &input,
202 &output,
203 reduce_axis,
204 strategy,
205 dtypes,
206 inst,
207 address_type,
208 None,
209 )?;
210
211 unsafe {
212 reduce_kernel::launch_unchecked::<TensorArgs>(
213 client,
214 settings.cube_count,
215 settings.cube_dim,
216 settings.address_type,
217 settings.vector.vector_size_input,
218 settings.vector.vector_size_output,
219 input.into_tensor_arg(),
220 output.into_tensor_arg(),
221 reduce_axis,
222 out_vec_axis,
223 blueprint,
224 inst,
225 dtypes.input,
226 dtypes.output,
227 dtypes.accumulation,
228 )
229 };
230
231 Ok(())
232}
233
234#[cube(launch_unchecked, address_type = "dynamic")]
235pub fn reduce_kernel<
236 In: Numeric,
237 InSize: Size,
238 Out: Numeric,
239 OutSize: Size,
240 Acc: Numeric,
241 RA: ReduceArgs,
242>(
243 input: &RA::Input<In, InSize>,
244 output: &mut RA::Output<Out, OutSize>,
245 reduce_axis: usize,
246 out_vec_axis: usize,
247 #[comptime] blueprint: ReduceBlueprint,
248 #[comptime] config: ReduceOperationConfig,
249 #[define(In)] _input_dtype: ElemType,
250 #[define(Out)] _output_dtype: ElemType,
251 #[define(Acc)] _acc_dtype: ElemType,
252) {
253 let (input, mut output) = init_tensors::<RA, In, InSize, Out, OutSize>(input, output);
254 reduce_kernel_virtual::<In, InSize, Out, OutSize, Acc>(
255 &input,
256 &mut output,
257 reduce_axis,
258 out_vec_axis,
259 blueprint,
260 config,
261 );
262}
263
264#[allow(clippy::too_many_arguments)]
272pub(crate) fn launch_reduce_with_indices(
273 client: &Client,
274 input: TensorBinding,
275 values: TensorBinding,
276 indices: TensorBinding,
277 reduce_axis: usize,
278 strategy: ReduceStrategy,
279 dtypes: ReduceWithIndicesDtypes,
280 operation: ReduceOperationConfig,
281) -> Result<(), ReduceError> {
282 match operation {
283 ReduceOperationConfig::TopK(k) | ReduceOperationConfig::ArgTopK(k) => launch_fused::<TopK>(
284 client,
285 input,
286 values,
287 indices,
288 reduce_axis,
289 strategy,
290 dtypes,
291 TopKConfig {
292 k,
293 output: ReduceOutputMode::Indices,
294 },
295 ReduceOperationConfig::ArgTopK(k),
296 ),
297 ReduceOperationConfig::Max | ReduceOperationConfig::ArgMax => launch_fused::<Max>(
298 client,
299 input,
300 values,
301 indices,
302 reduce_axis,
303 strategy,
304 dtypes,
305 ReduceOutputMode::Indices,
306 ReduceOperationConfig::ArgMax,
307 ),
308 ReduceOperationConfig::Min | ReduceOperationConfig::ArgMin => launch_fused::<Min>(
309 client,
310 input,
311 values,
312 indices,
313 reduce_axis,
314 strategy,
315 dtypes,
316 ReduceOutputMode::Indices,
317 ReduceOperationConfig::ArgMin,
318 ),
319 _ => unreachable!("reduce_with_indices rejects operations without indices"),
320 }
321}
322
323#[allow(clippy::too_many_arguments)]
326fn launch_fused<R: ReduceWithIndicesFamily>(
327 client: &Client,
328 input: TensorBinding,
329 values: TensorBinding,
330 indices: TensorBinding,
331 reduce_axis: usize,
332 strategy: ReduceStrategy,
333 dtypes: ReduceWithIndicesDtypes,
334 config: R::Config,
335 blueprint_operation: ReduceOperationConfig,
336) -> Result<(), ReduceError> {
337 let address_type = input
338 .required_address_type(dtypes.input.size())
339 .max(values.required_address_type(dtypes.values.size()))
340 .max(indices.required_address_type(dtypes.indices.size()));
341
342 let (blueprint, settings, out_vec_axis) = prepare_reduce_launch(
343 client,
344 &input,
345 &values,
346 reduce_axis,
347 strategy,
348 dtypes.values_dtypes(),
349 blueprint_operation,
354 address_type,
355 Some(dtypes.indices),
358 )?;
359
360 unsafe {
361 reduce_with_indices_kernel::launch_unchecked::<TensorArgs, R>(
362 client,
363 settings.cube_count,
364 settings.cube_dim,
365 settings.address_type,
366 settings.vector.vector_size_input,
367 settings.vector.vector_size_output,
368 settings.vector.vector_size_output,
369 input.into_tensor_arg(),
370 values.into_tensor_arg(),
371 indices.into_tensor_arg(),
372 reduce_axis,
373 out_vec_axis,
374 blueprint,
375 config,
376 dtypes.input,
377 dtypes.values,
378 dtypes.indices,
379 dtypes.accumulation,
380 )
381 };
382
383 Ok(())
384}
385
386#[cube(launch_unchecked, address_type = "dynamic")]
391pub fn reduce_with_indices_kernel<
392 In: Numeric,
393 InSize: Size,
394 Out: Numeric,
395 OutSize: Size,
396 Idx: Numeric,
397 IdxSize: Size,
398 Acc: Numeric,
399 RA: ReduceArgs,
400 R: ReduceWithIndicesFamily,
401>(
402 input: &RA::Input<In, InSize>,
403 output: &mut RA::Output<Out, OutSize>,
404 indices: &mut RA::Output<Idx, IdxSize>,
405 reduce_axis: usize,
406 out_vec_axis: usize,
407 #[comptime] blueprint: ReduceBlueprint,
408 #[comptime] config: R::Config,
409 #[define(In)] _input_dtype: ElemType,
410 #[define(Out)] _output_dtype: ElemType,
411 #[define(Idx)] _indices_dtype: ElemType,
412 #[define(Acc)] _acc_dtype: ElemType,
413) {
414 let (input_values, mut output) = init_tensors::<RA, In, InSize, Out, OutSize>(input, output);
415 let (_input_indices, mut indices) =
418 init_tensors::<RA, In, InSize, Idx, IdxSize>(input, indices);
419
420 reduce_with_indices_kernel_inner::<(In, InSize, Acc), (Out, OutSize), (Idx, IdxSize), R>(
421 &input_values,
422 &mut output,
423 &mut indices,
424 reduce_axis,
425 out_vec_axis,
426 blueprint,
427 config,
428 );
429}
430
431#[cube]
432fn reduce_with_indices_kernel_inner<
433 P: ReducePrecision,
434 Out: NumericVector,
435 Idx: NumericVector,
436 R: ReduceWithIndicesFamily,
437>(
438 input: &VirtualTensor<P::EI, P::SI>,
439 output: &mut VirtualTensor<Out::T, Out::N, ReadWrite>,
440 indices: &mut VirtualTensor<Idx::T, Idx::N, ReadWrite>,
441 reduce_axis: usize,
442 out_vec_axis: usize,
443 #[comptime] blueprint: ReduceBlueprint,
444 #[comptime] config: R::Config,
445) {
446 let inst = R::Instruction::<P>::from_config(config);
447
448 match blueprint.global {
449 GlobalReduceBlueprint::Cube(cube) => {
450 GlobalFullCubeReduce::execute_with_indices::<P, Out, Idx, R::Instruction<P>>(
451 input,
452 output,
453 indices,
454 reduce_axis,
455 out_vec_axis,
456 &inst,
457 blueprint.vectorization_mode,
458 cube,
459 )
460 }
461 GlobalReduceBlueprint::Plane(plane) => {
462 GlobalFullPlaneReduce::execute_with_indices::<P, Out, Idx, R::Instruction<P>>(
463 input,
464 output,
465 indices,
466 reduce_axis,
467 out_vec_axis,
468 &inst,
469 blueprint.vectorization_mode,
470 plane,
471 )
472 }
473 GlobalReduceBlueprint::Unit(unit) => {
474 GlobalFullUnitReduce::execute_with_indices::<P, Out, Idx, R::Instruction<P>>(
475 input,
476 output,
477 indices,
478 reduce_axis,
479 out_vec_axis,
480 &inst,
481 blueprint.vectorization_mode,
482 unit,
483 )
484 }
485 };
486}
487
488#[cube]
489pub fn reduce_kernel_virtual<
490 In: Numeric,
491 InSize: Size,
492 Out: Numeric,
493 OutSize: Size,
494 Acc: Numeric,
495>(
496 input: &VirtualTensor<In, InSize>,
497 output: &mut VirtualTensor<Out, OutSize, ReadWrite>,
498 reduce_axis: usize,
499 out_vec_axis: usize,
500 #[comptime] blueprint: ReduceBlueprint,
501 #[comptime] config: ReduceOperationConfig,
502) {
503 reduce_kernel_inner::<(In, InSize, Acc), (Out, OutSize), ReduceOperation>(
504 input,
505 output,
506 reduce_axis,
507 out_vec_axis,
508 blueprint,
509 config,
510 )
511}
512
513#[cube]
514fn reduce_kernel_inner<P: ReducePrecision, Out: NumericVector, R: ReduceFamily>(
515 input: &VirtualTensor<P::EI, P::SI>,
516 output: &mut VirtualTensor<Out::T, Out::N, ReadWrite>,
517 reduce_axis: usize,
518 out_vec_axis: usize,
519 #[comptime] blueprint: ReduceBlueprint,
520 #[comptime] config: R::Config,
521) {
522 let inst = R::Instruction::<P>::from_config(config);
523
524 match blueprint.global {
525 GlobalReduceBlueprint::Cube(cube) => {
526 GlobalFullCubeReduce::execute::<P, Out, R::Instruction<P>>(
527 input,
528 output,
529 reduce_axis,
530 out_vec_axis,
531 &inst,
532 blueprint.vectorization_mode,
533 cube,
534 )
535 }
536 GlobalReduceBlueprint::Plane(plane) => {
537 GlobalFullPlaneReduce::execute::<P, Out, R::Instruction<P>>(
538 input,
539 output,
540 reduce_axis,
541 out_vec_axis,
542 &inst,
543 blueprint.vectorization_mode,
544 plane,
545 )
546 }
547 GlobalReduceBlueprint::Unit(unit) => {
548 GlobalFullUnitReduce::execute::<P, Out, R::Instruction<P>>(
549 input,
550 output,
551 reduce_axis,
552 out_vec_axis,
553 &inst,
554 blueprint.vectorization_mode,
555 unit,
556 )
557 }
558 };
559}