cubek_convolution/components/
selection.rs1use cubecl::{
2 client::Client,
3 ir::{ElemType, VectorSize},
4};
5use cubek_matmul::{
6 definition::{AccumulatorOperand, MatmulAvailabilityError, MatmulElems, MatmulVectorSizes},
7 multi_level::{
8 components::{stage::PartitionBuffering, tile::TileMatmulKind},
9 definition::{BatchMatmulBlueprint, TilingScheme, adjust_dtypes},
10 routines::{NUM_SM_APPROX, NUM_TENSOR_CORES_APPROX, find_instruction_size},
11 },
12};
13
14use cubek_matmul::multi_level::{stage::SwizzleMode, stage::SwizzleModes};
15
16use crate::components::ConvolutionProblem;
17
18pub(crate) fn find_stage_size_m_n(
24 m: usize,
25 n: usize,
26 num_sm: usize,
27 max_tensor_cores: usize,
28 instruction_m: usize,
29 instruction_n: usize,
30 stage_size_k: usize,
31) -> (usize, usize) {
32 let max_tiles_elems_m = 256 / instruction_m;
33 let max_tiles_elems_n = 256 / instruction_n;
34 let max_tiles_total_stage = 16 / stage_size_k;
35
36 let mut dim_num_tiles_m = max_tensor_cores
37 .min(max_tiles_elems_m)
38 .min(max_tiles_total_stage);
39
40 let mut dim_num_tiles_n = max_tensor_cores
41 .min(max_tiles_elems_n)
42 .min(max_tiles_total_stage);
43
44 let total_tiles_m = m.div_ceil(instruction_m);
45 let total_tiles_n = n.div_ceil(instruction_n);
46
47 while total_tiles_n < dim_num_tiles_n && dim_num_tiles_n > 1 {
48 dim_num_tiles_n /= 2;
49 }
50
51 let total_tiles = total_tiles_m * total_tiles_n;
52
53 let mut stage_num_tiles = dim_num_tiles_m * dim_num_tiles_n;
54 let mut num_cubes_expected = total_tiles.div_ceil(stage_num_tiles);
55
56 let mut previous_dim_num_tiles = dim_num_tiles_m;
58 let mut previous_num_cubes = num_cubes_expected;
59
60 while num_cubes_expected < num_sm && dim_num_tiles_m > 1 {
62 previous_dim_num_tiles = dim_num_tiles_m;
63 previous_num_cubes = num_cubes_expected;
64
65 dim_num_tiles_m = dim_num_tiles_m.div_ceil(2);
67 stage_num_tiles = dim_num_tiles_m * dim_num_tiles_n;
68
69 num_cubes_expected = total_tiles.div_ceil(stage_num_tiles);
71 }
72
73 if (previous_num_cubes as isize - num_sm as isize).abs()
75 <= (num_cubes_expected as isize - num_sm as isize).abs()
76 {
77 (previous_dim_num_tiles, dim_num_tiles_n)
78 } else {
79 (dim_num_tiles_n, dim_num_tiles_m)
80 }
81}
82
83pub fn convolution_matmul_selection(
84 tile_matmul: TileMatmulKind,
85 client: &Client,
86 problem: &ConvolutionProblem,
87 plane_dim: u32,
88 swizzle: bool,
89 vector_sizes: &MatmulVectorSizes,
90 dtypes: &mut MatmulElems,
91) -> Result<BatchMatmulBlueprint, MatmulAvailabilityError> {
92 adjust_dtypes(client, dtypes, tile_matmul.requires_accelerator());
93
94 let stage_k = if problem.k >= 4096 { 4 } else { 2 };
97
98 let tile_size = find_instruction_size::<_, _>(
99 client,
100 (
101 dtypes.lhs_register,
102 dtypes.rhs_register,
103 dtypes.acc_register,
104 ),
105 (problem.m, problem.n, problem.k).into(),
106 (None, None, None),
107 |c, cfg| tile_matmul.is_supported(c, cfg),
108 |c, l, r, a| tile_matmul.supported_sizes(c, l, r, a),
109 )?;
110
111 let hardware = &client.properties().hardware;
112 let num_sm = hardware
113 .num_streaming_multiprocessors
114 .unwrap_or(NUM_TENSOR_CORES_APPROX);
115 let max_tensor_cores = hardware.num_tensor_cores.unwrap_or(NUM_SM_APPROX);
116
117 let (stage_size_m, stage_size_n) = find_stage_size_m_n(
118 problem.m,
119 problem.n,
120 num_sm as usize,
121 max_tensor_cores as usize,
122 tile_size.m() as usize,
123 tile_size.n() as usize,
124 stage_k as usize,
125 );
126
127 let tiling_scheme = TilingScheme::builder()
128 .with_stage_size((stage_size_m as u32, 1, 1).into())
129 .with_tile_size(tile_size)
130 .with_partition_size((1, stage_size_n as u32, stage_k).into())
131 .build()
132 .unwrap();
133
134 let mut builder = BatchMatmulBlueprint::builder(
135 tile_matmul,
136 tiling_scheme,
137 plane_dim,
138 &problem.as_matmul_problem(AccumulatorOperand::Absent),
141 )
142 .partition_buffering(PartitionBuffering::Single);
143
144 if swizzle {
145 let swizzle_dim = tiling_scheme.elements_per_stage_along_k() as usize;
146
147 let lhs = select_swizzle(swizzle_dim, dtypes.lhs_stage, vector_sizes.lhs);
148 let rhs = select_swizzle(swizzle_dim, dtypes.rhs_stage, vector_sizes.rhs);
149 builder = builder.shared_swizzle(SwizzleModes {
150 lhs,
151 rhs,
152 ..Default::default()
153 });
154 }
155
156 Ok(builder.build())
157}
158
159const SWIZZLE_ATOM: usize = 16;
161
162fn select_swizzle(swizzle_dim: usize, elem: ElemType, vector_size: VectorSize) -> SwizzleMode {
163 if elem.size() * vector_size > SWIZZLE_ATOM {
165 return SwizzleMode::None;
166 }
167 let swizzle_dim_bytes = swizzle_dim * elem.size();
168 if !swizzle_dim_bytes.is_power_of_two() {
169 return SwizzleMode::None;
170 }
171 match swizzle_dim_bytes {
172 32 => SwizzleMode::B32,
173 64 => SwizzleMode::B64,
174 _ => SwizzleMode::B128,
175 }
177}