burn_cubecl/kernel/index/
slice.rs1use crate::{
2 kernel::utils::{address_type, shape_divmod},
3 ops::numeric::empty_device_dtype,
4 tensor::CubeTensor,
5};
6use burn_backend::cubecl::dtype_to_storage_type;
7use burn_backend::{Slice, TensorMetadata};
8use burn_std::{Metadata, SliceOps};
9use cubecl::{
10 calculate_cube_count_elemwise, intrinsic,
11 prelude::*,
12 std::{FastDivmod, tensor::layout::linear::LinearViewMut},
13};
14use std::ops::Range;
15
16pub fn slice(tensor: CubeTensor, indices: &[Range<usize>]) -> CubeTensor {
18 let tensor = crate::kernel::untile(tensor);
19 let mut dims = tensor.shape();
20 let mut offset_start = 0u64;
21 let mut offset_end = 0u64;
22
23 for i in 0..indices.len() {
24 offset_start += (tensor.meta.strides()[i] * indices[i].start) as u64;
25 offset_end += (tensor.meta.strides()[i] * (dims[i] - indices[i].end)) as u64;
26 dims[i] = indices[i].end - indices[i].start;
27 }
28
29 let offset_start = offset_start * tensor.dtype.size() as u64;
30 let offset_end = offset_end * tensor.dtype.size() as u64;
31
32 let memory_offset_alignment = tensor.client.properties().memory.alignment;
33
34 if offset_start.is_multiple_of(memory_offset_alignment)
35 && offset_end.is_multiple_of(memory_offset_alignment)
36 {
37 CubeTensor::new(
38 tensor.client.clone(),
39 tensor
40 .handle
41 .clone()
42 .offset_start(offset_start)
43 .offset_end(offset_end),
44 Metadata::new(dims, tensor.meta.strides.clone()),
45 tensor.device.clone(),
46 tensor.dtype,
47 )
48 } else {
49 let output = empty_device_dtype(
50 tensor.client.clone(),
51 tensor.device.clone(),
52 dims,
53 tensor.dtype,
54 );
55 slice_on_output(tensor, output, indices)
56 }
57}
58
59#[cube(launch_unchecked, address_type = "dynamic")]
60fn slice_kernel<E: Numeric>(
61 input: &Tensor<E>,
62 mut output: LinearViewMut<'_, E>,
63 out_shape: Sequence<FastDivmod<usize>>,
64 indices: Sequence<usize>,
65 #[define(E)] _dtype: ElemType,
66) {
67 if !output.is_in_bounds(ABSOLUTE_POS) {
68 terminate!();
69 }
70
71 let rank = comptime![out_shape.len()];
72 let mut offset_output = ABSOLUTE_POS;
73 let mut offset_input = 0;
74
75 #[unroll]
76 for i in 0..rank {
77 let dim = rank - i - 1;
79
80 let range_start = indices[dim];
81 let (rem, offset_local) = out_shape[dim].div_mod(offset_output);
82 offset_output = rem;
83
84 let offset_local = offset_local + range_start;
85
86 offset_input += offset_local * input.stride(dim);
87 }
88
89 output.write(ABSOLUTE_POS, input[offset_input]);
90}
91
92pub(crate) fn slice_on_output(
93 tensor: CubeTensor,
94 output: CubeTensor,
95 indices: &[Range<usize>],
96) -> CubeTensor {
97 let ndims = tensor.meta.num_dims();
98 let mut indices_sequence = SequenceArg::<usize>::new();
99
100 for i in 0..ndims {
101 let start = indices.get(i).map(|index| index.start).unwrap_or(0);
102 indices_sequence.push(start);
103 }
104
105 let working_units = output.meta.num_elements();
106 let cube_dim = CubeDim::new(&tensor.client, working_units);
107 let cube_count = calculate_cube_count_elemwise(&tensor.client, working_units, cube_dim);
108 let dtype = tensor.dtype;
109
110 unsafe {
111 slice_kernel::launch_unchecked(
112 &output.client,
113 cube_count,
114 cube_dim,
115 address_type!(tensor, output),
116 tensor.into_tensor_arg(),
117 output.clone().into_linear_view(),
118 shape_divmod(&output),
119 indices_sequence,
120 dtype_to_storage_type(dtype),
121 )
122 };
123
124 output
125}
126
127#[cube(launch_unchecked, address_type = "dynamic")]
129fn slice_with_steps_kernel<E: Numeric>(
130 input: &Tensor<E>,
131 mut output: LinearViewMut<'_, E>,
132 out_shape: Sequence<FastDivmod<usize>>,
133 starts: Sequence<usize>,
134 ends: Sequence<usize>,
135 steps: Sequence<i32>,
136 #[define(E)] _dtype: ElemType,
137) {
138 if !output.is_in_bounds(ABSOLUTE_POS) {
139 terminate!();
140 }
141
142 let rank = comptime![out_shape.len()];
143 let mut output_offset = ABSOLUTE_POS;
144 let mut input_offset = 0;
145
146 #[unroll]
148 for i in 0..rank {
149 let dim = rank - i - 1;
151 let start = starts[dim];
152 let end = ends[dim];
153 let step = steps[dim];
154
155 let (rem, output_idx) = out_shape[dim].div_mod(output_offset);
156 output_offset = rem;
157
158 let input_idx = if step > 0 {
159 start + output_idx * (step as usize)
161 } else {
162 let abs_step = (-step) as usize;
164 let end_minus_1 = end - 1;
165 end_minus_1 - output_idx * abs_step
166 };
167
168 input_offset += input_idx * input.stride(dim);
169 }
170
171 output.write(ABSOLUTE_POS, input[input_offset]);
172}
173
174pub fn slice_with_steps(tensor: CubeTensor, slices: &[Slice]) -> CubeTensor {
176 let all_steps_one = slices.iter().all(|info| info.step == 1);
178
179 if all_steps_one {
180 let simple_ranges: Vec<Range<usize>> = slices
182 .iter()
183 .enumerate()
184 .map(|(i, slice)| slice.to_range(tensor.meta.shape()[i]))
185 .collect();
186 return slice(tensor, &simple_ranges);
187 }
188
189 let shape_output = tensor.shape().slice(slices).unwrap();
191
192 let output = empty_device_dtype(
194 tensor.client.clone(),
195 tensor.device.clone(),
196 shape_output.clone(),
197 tensor.dtype,
198 );
199
200 let mut starts = SequenceArg::<usize>::new();
202 let mut ends = SequenceArg::<usize>::new();
203 let mut steps = SequenceArg::<i32>::new();
204
205 for (dim, slice) in slices.iter().enumerate() {
206 let range = slice.to_range(tensor.meta.shape()[dim]);
207 starts.push(range.start);
208 ends.push(range.end);
209 steps.push(slice.step as i32);
210 }
211
212 for dim in slices.len()..tensor.meta.num_dims() {
214 starts.push(0);
215 ends.push(tensor.meta.shape[dim]);
216 steps.push(1);
217 }
218
219 let working_units = shape_output.num_elements();
221 let cube_dim = CubeDim::new(&tensor.client, working_units);
222 let cube_count = calculate_cube_count_elemwise(&tensor.client, working_units, cube_dim);
223 let dtype = tensor.dtype;
224
225 unsafe {
226 slice_with_steps_kernel::launch_unchecked(
227 &output.client,
228 cube_count,
229 cube_dim,
230 address_type!(tensor, output),
231 tensor.into_tensor_arg(),
232 output.clone().into_linear_view(),
233 shape_divmod(&output),
234 starts,
235 ends,
236 steps,
237 dtype_to_storage_type(dtype),
238 );
239 }
240
241 output
242}
243
244#[allow(unused)]
246#[cube]
247fn unwrap(value: u32) -> comptime_type!(u32) {
248 intrinsic!(|_| value.constant().unwrap().as_u32())
249}