Skip to main content

cubek_std/tile/variants/instruction/
plane_vec.rs

1use cubecl::{define_size, prelude::*};
2
3use crate::{
4    MatrixLayout, StageIdent, TileSize,
5    tile::{SharedTile, Tile, TileKind, TileKindExpand, TileScope},
6};
7
8// plane_vec_mat's fragment inner vector size (= reduce_vector_size). Bound at
9// allocate time via `scope.register_size::<NPlaneVec>(reduce_vector_size)`.
10// Decoupled from the outer enum `V` so the fragment is sized by the tile impl's
11// needs, not the stage's vector size.
12define_size!(pub NPlaneVec);
13
14/// Plane-vec tile. Holds the per-unit fragment plus the minimal comptime data
15/// the tile body actually uses (`tile_size` for the n iteration, plus the
16/// register-size hookup info implicit in the [`NPlaneVec`] binding done at
17/// allocation time). The matmul-level configuration that produced these
18/// values lives in cubek-matmul as `PlaneVecMatInnerProduct`.
19#[derive(CubeType)]
20pub struct PlaneVecTile<N: Numeric> {
21    // Fragment inner size is `NPlaneVec` (= reduce_vector_size).
22    pub data: Array<Vector<N, NPlaneVec>>,
23    #[cube(comptime)]
24    pub matrix_layout: MatrixLayout,
25    #[cube(comptime)]
26    pub tile_size: TileSize,
27    /// Inner reduction vector size for `NPlaneVec`. Carried because
28    /// `planevec_write_to_shared` needs the extent at use-site (`NPlaneVec::value()`
29    /// isn't observable from a `#[cube]` callsite).
30    #[cube(comptime)]
31    pub reduce_vector_size: u32,
32}
33
34// Binds the plane_vec_mat fragment's inner vector size (`NPlaneVec`) to the
35// `reduce_vector_size` chosen by the tile config at allocation time.
36#[cube]
37#[allow(unused_variables)]
38fn register_reduce_vector_size(#[comptime] reduce_vector_size: u32) {
39    intrinsic!(|scope| {
40        scope.register_size::<NPlaneVec>(reduce_vector_size as usize);
41    });
42}
43
44#[cube]
45pub fn planevec_allocate_lhs<L: Numeric, Sc: TileScope>(
46    #[comptime] layout: MatrixLayout,
47    #[comptime] tile_size: TileSize,
48    #[comptime] reduce_vector_size: u32,
49) -> Tile<L, Sc> {
50    register_reduce_vector_size(reduce_vector_size);
51    Tile::from_kind(TileKind::new_PlaneVec(PlaneVecTile::<L> {
52        data: Array::new(1usize),
53        matrix_layout: layout,
54        tile_size,
55        reduce_vector_size,
56    }))
57}
58
59#[cube]
60pub fn planevec_allocate_rhs<R: Numeric, Sc: TileScope>(
61    #[comptime] layout: MatrixLayout,
62    #[comptime] tile_size: TileSize,
63    #[comptime] reduce_vector_size: u32,
64) -> Tile<R, Sc> {
65    register_reduce_vector_size(reduce_vector_size);
66    Tile::from_kind(TileKind::new_PlaneVec(PlaneVecTile::<R> {
67        data: Array::new(tile_size.n() as usize),
68        matrix_layout: layout,
69        tile_size,
70        reduce_vector_size,
71    }))
72}
73
74#[cube]
75pub fn planevec_allocate_acc<A: Numeric, Sc: TileScope>(
76    #[comptime] layout: MatrixLayout,
77    #[comptime] tile_size: TileSize,
78    #[comptime] reduce_vector_size: u32,
79) -> Tile<A, Sc> {
80    register_reduce_vector_size(reduce_vector_size);
81    Tile::from_kind(TileKind::new_PlaneVec(PlaneVecTile::<A> {
82        data: Array::new(tile_size.n() as usize),
83        matrix_layout: layout,
84        tile_size,
85        reduce_vector_size,
86    }))
87}
88
89#[cube]
90impl<A: Numeric> PlaneVecTile<A> {
91    /// Executes `lhs ยท rhs`, accumulating into `self` via the plane-vec
92    /// inner-product matmul.
93    pub fn mma<L: Numeric, R: Numeric>(&mut self, lhs: &PlaneVecTile<L>, rhs: &PlaneVecTile<R>) {
94        planevec_execute(&lhs.data, &rhs.data, &mut self.data, self.tile_size);
95    }
96}
97
98#[cube]
99impl<N: Numeric> PlaneVecTile<N> {
100    /// Copies into the plane-vec tile from `source`. Supported sources:
101    /// `Shared` and `None` (zero-init).
102    pub fn copy_from<SE: Numeric, SS: Size, Sc: TileScope>(
103        &mut self,
104        source: &Tile<SE, Sc>,
105        #[comptime] ident: StageIdent,
106    ) {
107        match &source.kind {
108            TileKind::SharedTile(shared) => {
109                planevec_load_from_shared::<SE, SS, N>(
110                    shared,
111                    &mut self.data,
112                    self.tile_size,
113                    ident,
114                );
115            }
116            TileKind::None => planevec_load_zeros::<N>(&mut self.data, self.tile_size),
117            TileKind::Cmma(_)
118            | TileKind::Mma(_)
119            | TileKind::Register(_)
120            | TileKind::PlaneVec(_)
121            | TileKind::Interleaved(_)
122            | TileKind::Unit(_)
123            | TileKind::WhiteboxFragment(_)
124            | TileKind::RowWise(_)
125            | TileKind::Bounce(_)
126            | TileKind::Stage(_)
127            | TileKind::Partition(_)
128            | TileKind::Pipelined(_) => {
129                panic!("PlaneVecTile::copy_from: unsupported source variant")
130            }
131        }
132    }
133
134    pub fn init_zero(&mut self) {
135        planevec_load_zeros::<N>(&mut self.data, self.tile_size);
136    }
137}
138
139// ===========================================================================
140// Compute: matmul / load / write / zero-init
141// ===========================================================================
142
143#[cube]
144pub fn planevec_execute<L: Numeric, R: Numeric, A: Numeric>(
145    lhs: &Array<Vector<L, NPlaneVec>>,
146    rhs: &Array<Vector<R, NPlaneVec>>,
147    acc: &mut Array<Vector<A, NPlaneVec>>,
148    #[comptime] tile_size: TileSize,
149) {
150    let n = tile_size.n();
151    #[unroll]
152    for n_idx in 0..n as usize {
153        let mut acc_vec = acc[n_idx];
154        #[unroll]
155        for vi in 0..NPlaneVec::value() {
156            let lhs_elem = A::cast_from(lhs[0].extract(vi));
157            let rhs_elem = A::cast_from(rhs[n_idx].extract(vi));
158            acc_vec.insert(vi, acc_vec.extract(vi) + plane_sum(lhs_elem * rhs_elem));
159        }
160        acc[n_idx] = acc_vec;
161    }
162}
163
164#[cube]
165pub fn planevec_load_from_shared<E: Numeric, ES: Size, N: Numeric>(
166    shared: &SharedTile<E>,
167    arr: &mut Array<Vector<N, NPlaneVec>>,
168    #[comptime] tile_size: TileSize,
169    #[comptime] ident: StageIdent,
170) {
171    let shared = shared.view::<ES>();
172    let shared = &shared;
173    match ident {
174        StageIdent::Lhs => {
175            let offset = shared.stage_offset(UNIT_POS_X);
176            arr[0] = Vector::cast_from(shared.container[offset as usize]);
177        }
178        StageIdent::Rhs | StageIdent::Acc => {
179            let n = tile_size.n();
180            #[unroll]
181            for n_idx in 0..n {
182                let offset = shared.stage_offset(UNIT_POS_X + n_idx * shared.stride);
183                arr[n_idx as usize] = Vector::cast_from(shared.container[offset as usize]);
184            }
185        }
186        _ => panic!("Invalid ident for PlaneVec load"),
187    }
188}
189
190#[cube]
191pub fn planevec_load_zeros<N: Numeric>(
192    arr: &mut Array<Vector<N, NPlaneVec>>,
193    #[comptime] tile_size: TileSize,
194) {
195    let n = tile_size.n();
196    let zero = N::from_int(0);
197    #[unroll]
198    for n_idx in 0..n as usize {
199        arr[n_idx] = Vector::cast_from(zero);
200    }
201}
202
203#[cube]
204pub fn planevec_write_to_shared<A: Numeric, E: Numeric, ES: Size>(
205    shared: &mut SharedTile<E>,
206    arr: &Array<Vector<A, NPlaneVec>>,
207    #[comptime] tile_size: TileSize,
208    #[comptime] reduce_vector_size: u32,
209) {
210    let mut shared = shared.view::<ES>();
211    let shared = &mut shared;
212    if UNIT_POS_X == 0 {
213        let out_vector_size = shared.container.vector_size().comptime();
214        let n = tile_size.n();
215        let total_out_vectors = n as usize / out_vector_size;
216        let reduce_vec = reduce_vector_size as usize;
217
218        #[unroll]
219        for out_vector_iter in 0..total_out_vectors {
220            let mut out_vector = Vector::<E, ES>::empty();
221            #[unroll]
222            for within_vector in 0..out_vector_size {
223                let n_idx = out_vector_iter * out_vector_size + within_vector;
224                let acc_vec = arr[n_idx];
225                let mut sum = A::from_int(0);
226                for i in 0..reduce_vec {
227                    sum += acc_vec.extract(i);
228                }
229                out_vector.insert(within_vector, E::cast_from(sum));
230            }
231            let offset = shared.stage_offset(out_vector_iter as u32);
232            shared.container[offset as usize] = out_vector;
233        }
234    }
235}