cubecl_std/tensor/contiguous/
perpendicular.rs1use cubecl::prelude::*;
2use cubecl_core::{
3 self as cubecl, calculate_cube_count_elemwise, ir::VectorSize, tensor_vector_size_parallel,
4 tensor_vector_size_perpendicular,
5};
6use std::cmp::min;
7
8#[cube(launch_unchecked, address_type = "dynamic")]
19fn copy_perpendicular<T: Numeric, N: Size>(
20 input: &Tensor<Vector<T, N>>,
21 output: &mut Tensor<Vector<T, N>>,
22 blocks_last: usize,
23 blocks_axis: usize,
24 rows: usize,
25 blocks_per_unit: usize,
26 tiles_last: usize,
27 panel_rows: usize,
28 num_tiles: usize,
29 axis: usize,
30 #[comptime] rank: usize,
31 #[define(T)] _elem: ElemType,
32) {
33 let tile = CUBE_POS;
34 if tile >= num_tiles {
35 terminate!()
36 }
37
38 let vector_size = input.vector_size();
39 let last_axis = rank - 1;
40
41 let panel_tiles = panel_rows * tiles_last;
46 let tile_in_panel = tile % panel_tiles;
47 let tile_rows_in_panel = tile_in_panel % panel_rows;
48 let tile_rows = (tile / panel_tiles) * panel_rows + tile_rows_in_panel;
49 let tile_last = (tile_in_panel / panel_rows + tile_rows_in_panel) % tiles_last;
50 let row = tile_rows * CUBE_DIM_Y as usize + UNIT_POS_Y as usize;
51 let block_start = (tile_last * CUBE_DIM_X as usize + UNIT_POS_X as usize) * blocks_per_unit;
52 if row >= rows || block_start >= blocks_last {
53 terminate!()
54 }
55 let mut block_end = block_start + blocks_per_unit;
56 if block_end > blocks_last {
57 block_end = blocks_last;
58 }
59
60 let block_axis = row % blocks_axis;
61 let mut batch = row / blocks_axis;
62 let mut offset_input = 0;
63 let mut offset_output = 0;
64 #[unroll]
65 for i in 0..rank {
66 let dim = rank - 1 - i;
67 if dim != axis && dim != last_axis {
68 let coordinate = batch % output.shape(dim);
69 batch /= output.shape(dim);
70 offset_input += coordinate * input.stride(dim);
71 offset_output += coordinate * output.stride(dim);
72 }
73 }
74
75 let start_axis = block_axis * vector_size;
76 let input_stride = input.stride(last_axis);
77 let output_stride = output.stride(axis);
78 let index_input = (offset_input + start_axis) / vector_size;
79 let index_output = (offset_output + start_axis * output_stride) / vector_size;
80 let input_stride = input_stride / vector_size;
81 let output_stride = output_stride / vector_size;
82
83 let mut accumulators = Sequence::<Vector<T, N>>::new();
84
85 #[unroll]
86 for _ in 0..vector_size {
87 accumulators.push(Vector::empty());
88 }
89
90 for block in block_start..block_end {
91 let index_input = index_input + block * vector_size * input_stride;
94 let index_output = index_output + block;
95
96 #[unroll]
97 for i in 0..vector_size {
98 let gathered = input[index_input + i * input_stride];
99
100 #[unroll]
101 for o in 0..vector_size {
102 let vector = accumulators.index_mut(o);
103 vector.insert(i, gathered.extract(o));
104 }
105 }
106
107 #[unroll]
108 for o in 0..vector_size {
109 output[index_output + o * output_stride] = accumulators[o];
110 }
111 }
112}
113
114pub fn try_launch_copy_perpendicular(
120 client: &Client,
121 input: &TensorBinding,
122 output: &TensorBinding,
123 dtype: ElemType,
124) -> bool {
125 if output.shape.contains(&0) {
127 return false;
128 }
129 let (axis, vector_size) = perpendicular_vector_size(client, input, output, dtype);
130 if vector_size == 1 {
131 return false;
132 }
133
134 let rank = output.shape.len();
135 let blocks_last = output.shape[rank - 1] / vector_size;
136 let blocks_axis = output.shape[axis] / vector_size;
137 let batches =
138 output.shape.iter().product::<usize>() / (output.shape[rank - 1] * output.shape[axis]);
139 let rows = batches * blocks_axis;
140
141 let hardware = &client.properties().hardware;
142 let (blocks_per_unit, cube_dim) = if hardware.num_cpu_cores.is_some() {
143 let cube_dim = CubeDim::new(client, rows);
146 (blocks_last, CubeDim::new_2d(1, cube_dim.num_elems()))
147 } else {
148 let units = prev_power_of_two(hardware.max_units_per_cube.min(256) as usize);
152 let (max_x, max_y, _) = hardware.max_cube_dim;
153 let max_x = prev_power_of_two(max_x as usize);
154 let max_y = prev_power_of_two(max_y as usize);
155 let blocks_last_pow2 = blocks_last.next_power_of_two().min(max_x);
156 let cube_x = blocks_last_pow2.min(8).min(units);
157 let cube_y = rows.next_power_of_two().min(units / cube_x).min(max_y);
158 let cube_x = blocks_last_pow2.min(units / cube_y);
159 (1, CubeDim::new_2d(cube_x as u32, cube_y as u32))
160 };
161
162 let tiles_last = blocks_last
163 .div_ceil(blocks_per_unit)
164 .div_ceil(cube_dim.x as usize);
165 let tiles_rows = rows.div_ceil(cube_dim.y as usize);
166 let panel_rows = blocks_axis.div_ceil(cube_dim.y as usize);
167 let num_tiles = tiles_rows.div_ceil(panel_rows) * panel_rows * tiles_last;
169 let cube_count = calculate_cube_count_elemwise(client, num_tiles, CubeDim::new_1d(1));
170 let address_type = input
171 .required_address_type(dtype.size())
172 .max(output.required_address_type(dtype.size()));
173
174 unsafe {
175 copy_perpendicular::launch_unchecked(
176 client,
177 cube_count,
178 cube_dim,
179 address_type,
180 vector_size,
181 input.clone().into_tensor_arg(),
182 output.clone().into_tensor_arg(),
183 blocks_last,
184 blocks_axis,
185 rows,
186 blocks_per_unit,
187 tiles_last,
188 panel_rows,
189 num_tiles,
190 axis,
191 rank,
192 dtype,
193 );
194 }
195
196 true
197}
198
199fn perpendicular_vector_size(
201 client: &Client,
202 input: &TensorBinding,
203 output: &TensorBinding,
204 dtype: ElemType,
205) -> (usize, VectorSize) {
206 let axis = input
209 .strides
210 .iter()
211 .zip(input.shape.iter())
212 .position(|(stride, shape)| *stride == 1 && *shape > 1)
213 .unwrap_or(0);
214 let rank = output.shape.len();
215
216 let vector_size_perpendicular = tensor_vector_size_perpendicular(
217 client.io_optimized_vector_sizes(dtype.size()),
218 &input.shape,
219 &input.strides,
220 rank - 1,
221 );
222 let vector_size_parallel = tensor_vector_size_parallel(
223 client.io_optimized_vector_sizes(dtype.size()),
224 &output.shape,
225 &output.strides,
226 rank - 1,
227 );
228 let vector_size_axis = tensor_vector_size_parallel(
231 client.io_optimized_vector_sizes(dtype.size()),
232 &input.shape,
233 &input.strides,
234 axis,
235 );
236 let vector_size = min(
237 min(vector_size_perpendicular, vector_size_parallel),
238 vector_size_axis,
239 );
240
241 (axis, vector_size)
242}
243
244fn prev_power_of_two(value: usize) -> usize {
246 1 << value.ilog2()
247}