cubecl_tile/tile/accumulator/
smem_cyclic.rs1use core::marker::PhantomData;
15
16use cubecl::ir::{ExpandValue, VectorSize};
17use cubecl::prelude::*;
18use cubecl::unexpanded;
19use cubecl_std::tensor::{
20 ErasedTensor, ErasedTensorExpand, ErasedTensorOperationsExpand, ReadsLines, WriteOnly,
21 WritesLines,
22};
23
24use crate::*;
25
26#[derive(CubeType)]
37pub struct SmemCyclicAccumulation<A: Numeric> {
38 values: Shared<[A]>,
39 like: Tile,
41 pub source: Tile,
43 #[cube(comptime)]
44 writers: usize,
45}
46
47#[cube]
48impl Tile {
49 pub fn smem_cyclic_accumulation<A: Numeric>(
54 &self,
55 #[comptime] writers: usize,
56 ) -> SmemCyclicAccumulation<A> {
57 let define!(Served) = self.element;
58 let space = comptime!(self.place.space.clone());
59 let width = self.vector_size();
60 let size!(W) = width;
61 let form = comptime!(StageForm::dense(
62 &space,
63 width,
64 StageStorage::Strided,
65 LineBytes(A::size().comptime() * width)
66 ));
67 let values = Shared::<[A]>::new_slice(comptime!(form.cells() * width));
68 sync_cube();
70
71 let units = self.units();
72 let source = Memory::smem_backed::<Served>(
73 space,
74 width,
75 comptime!(FillUnits::cube(units)),
76 Backing::new_ReadCall(RawCall::of::<Served>(
77 ErasedTensor::<Served, ReadOnly>::of_smem_plain_load::<A, W>(&values),
78 )),
79 comptime!(Packing::Plain),
80 comptime!(form.clone()),
81 RuntimeMap::integral(comptime!(form.physical_rank())),
82 ComptimeOption::new_None(),
83 comptime!(Write::Replace),
84 comptime!(Overhang::Never),
85 );
86 SmemCyclicAccumulation::<A> {
87 values,
88 like: self.clone(),
89 source: source.nested_like(self),
90 writers,
91 }
92 }
93}
94
95#[cube]
96impl<A: Numeric> SmemCyclicAccumulation<A> {
97 fn sink(&self, round: usize) -> Tile {
100 let space = comptime!(self.like.place.space.clone());
101 let width = self.like.vector_size();
102 let size!(W) = width;
103 let form = comptime!(StageForm::dense(
104 &space,
105 width,
106 StageStorage::Strided,
107 LineBytes(A::size().comptime() * width)
108 ));
109 let units = self.like.units();
110 let sink = Memory::smem_backed::<A>(
111 space,
112 width,
113 comptime!(FillUnits::cube(units)),
114 Backing::new_WriteCall(RawCall::of::<A>(
115 ErasedTensor::<A, WriteOnly>::of_smem_cyclic::<W>(&self.values, round == 0),
116 )),
117 comptime!(Packing::Plain),
118 comptime!(form.clone()),
119 RuntimeMap::integral(comptime!(form.physical_rank())),
120 ComptimeOption::new_None(),
121 comptime!(Write::Exclusive(Schedule::Cyclic)),
122 comptime!(Overhang::Masked),
123 );
124 sink.nested_like(&self.like)
125 }
126
127 pub fn drain(&self, partial: &Tile, writer: usize) {
132 let define!(Served) = partial.element;
133 #[unroll]
134 for round in 0..self.writers {
135 let turn = (writer + round) % comptime!(self.writers);
136 partial.drained_chunk_into::<Served, A>(
137 &self.sink(round),
138 turn,
139 comptime!(self.writers),
140 );
141 sync_cube();
142 }
143 }
144
145 pub fn drain_at(&self, partial: &Tile, window: &Region, writer: usize) {
148 let define!(Served) = partial.element;
149 #[unroll]
150 for round in 0..self.writers {
151 let turn = (writer + round) % comptime!(self.writers);
152 partial.drained_chunk_into::<Served, A>(
153 &self.sink(round).at(window),
154 turn,
155 comptime!(self.writers),
156 );
157 sync_cube();
158 }
159 }
160}
161
162pub(crate) trait SmemCyclicSink<E: Numeric> {
164 fn of_smem_cyclic<W: Size>(_values: &Shared<[E]>, _first: bool) -> ErasedTensor<E, WriteOnly> {
167 unexpanded!()
168 }
169
170 fn __expand_of_smem_cyclic<W: Size>(
171 _scope: &Scope,
172 values: &<Shared<[E]> as CubeType>::ExpandType,
173 first: NativeExpand<bool>,
174 ) -> ErasedTensorExpand<E, WriteOnly> {
175 ErasedTensorExpand::new(SmemCyclic::<E, W> {
176 values: ExpandTypeClone::clone_unchecked(values),
177 first,
178 _e: PhantomData,
179 })
180 }
181}
182
183impl<E: Numeric> SmemCyclicSink<E> for ErasedTensor<E, WriteOnly> {}
184
185pub(crate) trait SmemPlainLoadSource<T: Numeric> {
187 fn of_smem_plain_load<A: Numeric, W: Size>(_values: &Shared<[A]>) -> ErasedTensor<T, ReadOnly> {
189 unexpanded!()
190 }
191
192 fn __expand_of_smem_plain_load<A: Numeric, W: Size>(
193 _scope: &Scope,
194 values: &<Shared<[A]> as CubeType>::ExpandType,
195 ) -> ErasedTensorExpand<T, ReadOnly> {
196 ErasedTensorExpand::new(SmemPlainLoad::<A, T, W> {
197 values: ExpandTypeClone::clone_unchecked(values),
198 _t: PhantomData,
199 })
200 }
201}
202
203impl<T: Numeric> SmemPlainLoadSource<T> for ErasedTensor<T, ReadOnly> {}
204
205struct SmemCyclic<E: Numeric, W: Size> {
209 values: <Shared<[E]> as CubeType>::ExpandType,
210 first: NativeExpand<bool>,
211 _e: PhantomData<(E, W)>,
212}
213
214impl<E: Numeric, W: Size> ErasedTensorOperationsExpand<E> for SmemCyclic<E, W> {
215 fn __expand_vector_size_method(&self, scope: &Scope) -> VectorSize {
216 W::__expand_value(scope)
217 }
218
219 fn __expand_lines_method(&self, scope: &Scope) -> NativeExpand<usize> {
220 lines_of::expand::<E, W>(scope, &self.values)
221 }
222
223 fn __expand_write_line_method(
224 &mut self,
225 scope: &Scope,
226 index: NativeExpand<usize>,
227 value: ExpandValue,
228 ) {
229 cyclic_line::expand::<E, W>(scope, &mut self.values, index, value.into(), self.first);
230 }
231}
232
233impl<E: Numeric, W: Size> WritesLines<E> for SmemCyclic<E, W> {}
234
235struct SmemPlainLoad<A: Numeric, T: Numeric, W: Size> {
238 values: <Shared<[A]> as CubeType>::ExpandType,
239 _t: PhantomData<(T, W)>,
240}
241
242impl<A: Numeric, T: Numeric, W: Size> ErasedTensorOperationsExpand<T> for SmemPlainLoad<A, T, W> {
243 fn __expand_vector_size_method(&self, scope: &Scope) -> VectorSize {
244 W::__expand_value(scope)
245 }
246
247 fn __expand_lines_method(&self, scope: &Scope) -> NativeExpand<usize> {
248 lines_of::expand::<A, W>(scope, &self.values)
249 }
250
251 fn __expand_read_line_method(&self, scope: &Scope, index: NativeExpand<usize>) -> ExpandValue {
252 load_plain_line::expand::<A, T, W>(scope, &self.values, index).expand
253 }
254}
255
256impl<A: Numeric, T: Numeric, W: Size> ReadsLines<T> for SmemPlainLoad<A, T, W> {}
257
258#[cube]
260fn lines_of<E: Numeric, W: Size>(values: &Shared<[E]>) -> usize {
261 values.len() / W::value()
262}
263
264#[cube]
267fn cyclic_line<E: Numeric, W: Size>(
268 values: &mut Shared<[E]>,
269 index: usize,
270 value: Vector<E, W>,
271 first: bool,
272) {
273 #[unroll]
274 for j in 0..W::value() {
275 let cell = index * W::value() + j;
276 if first {
277 values[cell] = value.extract(j);
278 } else {
279 values[cell] += value.extract(j);
280 }
281 }
282}
283
284#[cube]
286fn load_plain_line<A: Numeric, T: Numeric, W: Size>(
287 values: &Shared<[A]>,
288 index: usize,
289) -> Vector<T, W> {
290 let mut line = Vector::<T, W>::empty();
291 #[unroll]
292 for j in 0..W::value() {
293 line.insert(j, T::cast_from(values[index * W::value() + j]));
294 }
295 line
296}