cubecl_tile/ops/
product.rs1use cubecl::prelude::*;
5
6use cubecl_std::tensor::layout::CoordsDyn;
7
8use crate::ops::contraction::resolve_nd_coords;
9use crate::*;
10
11#[cube]
12impl Tile {
13 pub fn product(&mut self, a: &Tile, b: &Tile) {
15 let define!(Served) = self.element;
16 let define!(Lhs) = a.read_element();
17 let define!(Rhs) = b.read_element();
18 self.mul_from::<Served, Lhs, Rhs>(a, b)
19 }
20
21 fn mul_from<D: Numeric, A: Numeric, B: Numeric>(&mut self, a: &Tile, b: &Tile) {
23 let space = comptime!(self.place.space.clone());
24 let width = self.vector_size();
25 let per_read = b.vector_size();
26 let split = comptime!(GroupWalk::of(&space, &b.place.space, width, per_read));
27 let size!(W) = width;
28 let size!(F) = per_read;
29 let a_reader = a.nd_split::<A, W>();
30 let b_reader = b.nd_split::<B, F>();
31
32 let a_step_at = comptime!(a.place.space.position(split.axis));
33 let extents = Coords::constant(comptime!(split.groups.clone()));
34 let total = comptime!(split.groups.iter().product::<usize>());
35
36 let mut dst = self.nd_mut::<D, W>();
37 let workers = CUBE_DIM as usize;
38 let mut i = UNIT_POS as usize;
39 while i < total {
40 let group = group_line(&extents.unravel(i.cast::<u32>()), comptime!(split.clone()));
41 let (a_base, b_base) = bases(
42 &group,
43 comptime!(space.clone()),
44 comptime!(a.place.space.clone()),
45 comptime!(b.place.space.clone()),
46 width,
47 per_read,
48 );
49
50 let scales = b_reader
52 .view
53 .read(b_reader.map.anchor(b_base, comptime!(Vec::new())));
54 let moving = comptime!(vec![split.axis]);
56 let a_anchor = a_reader
57 .map
58 .anchor(a_base.clone(), comptime!(moving.clone()));
59
60 #[unroll]
61 for f in 0..comptime!(split.per_read) {
62 let value = a_reader.view.read(a_reader.map.advance(
63 &a_anchor,
64 stepped(&a_base, comptime!(a_step_at), f),
65 comptime!(moving.clone()),
66 ));
67 let left = Vector::<D, W>::cast_from(value);
68 let right = Vector::<D, W>::cast_from(scales.extract(f));
69 dst.write(stepped(&group, comptime!(split.at), f), left * right);
70 }
71 i += workers;
72 }
73 }
74}
75
76#[derive(Clone, Debug)]
78struct GroupWalk {
79 axis: Axis,
81 at: usize,
83 per_read: usize,
85 groups: Vec<usize>,
87}
88
89impl GroupWalk {
90 fn of(dst: &Space, b: &Space, width: usize, per_read: usize) -> Self {
92 let rank = dst.rank();
93 let axis = b.axis_at(b.rank() - 1);
94 let at = dst.position(axis);
95 assert!(
96 per_read == 1 || at != rank - 1,
97 "Tile::mul: {per_read} of `b` arrive per read along {axis:?}, which is also the axis the \
98 destination's own lines run along, so one line would straddle two of them"
99 );
100 let mut groups = line_extents(dst, width, 0, rank);
101 assert!(
102 groups[at].is_multiple_of(per_read),
103 "Tile::mul: {} positions of {axis:?} do not divide into reads of {per_read}",
104 groups[at]
105 );
106 groups[at] /= per_read;
107 GroupWalk {
108 axis,
109 at,
110 per_read,
111 groups,
112 }
113 }
114}
115
116#[cube]
118fn group_line(at: &Coords<u32>, #[comptime] split: GroupWalk) -> CoordsDyn {
119 let mut out = CoordsDyn::new();
120 #[unroll]
121 for p in 0..comptime!(split.groups.len()) {
122 let coord = match comptime!(p == split.at) {
123 true => at.at(p).times(comptime!(split.per_read as u32)),
124 false => at.at(p),
125 };
126 out.push(coord);
127 }
128 out
129}
130
131#[cube]
133fn bases(
134 group: &CoordsDyn,
135 #[comptime] dst: Space,
136 #[comptime] a: Space,
137 #[comptime] b: Space,
138 #[comptime] width: usize,
139 #[comptime] per_read: usize,
140) -> (CoordsDyn, CoordsDyn) {
141 let mut cells = Coords::<u32>::new();
142 #[unroll]
143 for p in 0..comptime!(dst.rank()) {
144 cells.push(group[p].times(comptime!(match p == dst.rank() - 1 {
145 true => width as u32,
146 false => 1u32,
147 })));
148 }
149 let empty = Coords::<u32>::new();
150 let no_reduce = comptime!(Vec::<Axis>::new());
151 (
152 resolve_nd_coords(&a, &dst, &no_reduce, &cells, &empty, width, true),
153 resolve_nd_coords(&b, &dst, &no_reduce, &cells, &empty, per_read, true),
154 )
155}
156
157#[cube]
159fn stepped(base: &CoordsDyn, #[comptime] at: usize, #[comptime] f: usize) -> CoordsDyn {
160 let mut out = CoordsDyn::new();
161 #[unroll]
162 for p in 0..comptime!(base.len()) {
163 let coord = match comptime!(p == at) {
164 true => base[p] + comptime!(f as u32),
165 false => base[p],
166 };
167 out.push(coord);
168 }
169 out
170}