burn_cubecl/kernel/matmul/
base.rs1use super::init_matmul_output;
2use crate::{kernel::quantization::dequantize, tensor::CubeTensor};
3use burn_backend::cubecl::dtype_to_storage_type;
4use burn_backend::{DType, TensorMetadata};
5use burn_std::{MatmulTransformAnalysis, MatmulTransformPolicy};
6use cubek::{
7 matmul::{
8 definition::{MatmulElems, MatmulGlobalElems, MatmulSetupError},
9 strategy::Strategy,
10 },
11 std::InputBinding,
12};
13
14#[cfg(feature = "autotune")]
15use super::matmul_autotune;
16
17pub enum MatmulStrategy {
19 #[cfg(feature = "autotune")]
20 Autotune,
22 Cube,
24}
25
26impl Default for MatmulStrategy {
27 fn default() -> Self {
28 #[cfg(feature = "autotune")]
30 return MatmulStrategy::Autotune;
31
32 #[cfg(not(feature = "autotune"))]
33 MatmulStrategy::Cube
34 }
35}
36
37fn is_two_level(tensor: &CubeTensor) -> bool {
38 match tensor.dtype {
39 DType::QFloat(scheme) => burn_backend::quantization::global_scale_dtype(&scheme).is_some(),
40 _ => false,
41 }
42}
43
44fn maybe_dequantize(tensor: CubeTensor, dtype: DType) -> CubeTensor {
45 if is_two_level(&tensor) {
46 dequantize(tensor, dtype)
47 } else {
48 tensor
49 }
50}
51
52pub fn matmul(
54 lhs: CubeTensor,
55 rhs: CubeTensor,
56 out: Option<CubeTensor>,
57 strategy: MatmulStrategy,
58 out_dtype: DType,
59) -> Result<CubeTensor, MatmulSetupError> {
60 let out = out.unwrap_or_else(|| init_matmul_output(&lhs, &rhs, out_dtype));
61
62 let mut lhs = maybe_dequantize(lhs, out_dtype);
65 let rhs = maybe_dequantize(rhs, out_dtype);
66
67 let mut out_launch = out.clone();
73 if lhs.qparams.is_none() {
74 let analysis = MatmulTransformAnalysis::from_metadata(&lhs.meta, &rhs.meta, &out.meta);
75 let action = MatmulTransformPolicy::default().action(&analysis);
76 action.apply(&mut lhs.meta);
77 action.apply(&mut out_launch.meta);
78 }
79
80 match strategy {
81 MatmulStrategy::Cube => {
82 launch_matmul(&Strategy::default(), lhs, rhs, out_launch)?;
83 Ok(out)
84 }
85 #[cfg(feature = "autotune")]
86 MatmulStrategy::Autotune => {
87 matmul_autotune(lhs, rhs, Some(out_launch), out_dtype);
88 Ok(out)
89 }
90 }
91}
92
93pub(crate) fn launch_matmul_naive<S: Clone + Into<Strategy>>(
94 strategy: &S,
95 mut lhs: CubeTensor,
96 mut rhs: CubeTensor,
97 out: CubeTensor,
98) -> Result<(), MatmulSetupError> {
99 if lhs.qparams.is_some() || rhs.qparams.is_some() {
102 match launch_matmul(strategy, lhs.clone(), rhs.clone(), out.clone()) {
103 Err(_) => {
104 if lhs.qparams.is_some() {
105 lhs = dequantize(lhs, out.dtype);
106 }
107 if rhs.qparams.is_some() {
108 rhs = dequantize(rhs, out.dtype);
109 }
110 launch_matmul(strategy, lhs, rhs, out)
111 }
112 Ok(_) => Ok(()),
113 }
114 } else {
115 launch_matmul(strategy, lhs, rhs, out)
116 }
117}
118
119pub(crate) fn launch_matmul<S: Clone + Into<Strategy>>(
120 strategy: &S,
121 lhs: CubeTensor,
122 mut rhs: CubeTensor,
123 out: CubeTensor,
124) -> Result<(), MatmulSetupError> {
125 let strategy: Strategy = strategy.clone().into();
126 let client = &out.client;
127
128 let lhs_quant_handles = lhs.quantized_handles();
129 let out_dtype: DType = out.dtype;
130
131 let (lhs_dtype, lhs_handle) = match lhs_quant_handles {
132 None => {
133 let lhs_dtype = lhs.dtype;
134 (
135 lhs_dtype,
136 InputBinding::new(lhs.binding(), dtype_to_storage_type(lhs_dtype)),
137 )
138 }
139 Some((data, scale)) => {
140 let scheme = lhs.scheme();
141 let data_dtype = data.dtype;
142 let scale_dtype = scale.dtype;
143 (
144 out_dtype,
145 InputBinding::quantized(
146 data.binding(),
147 scale.binding(),
148 lhs.meta.shape().clone(),
149 scheme,
150 dtype_to_storage_type(data_dtype),
151 dtype_to_storage_type(scale_dtype),
152 ),
153 )
154 }
155 };
156
157 let rhs_quant_handles = rhs.quantized_handles();
158
159 let (rhs_dtype, rhs_handle) = match rhs_quant_handles {
160 None => (
161 lhs_dtype,
162 InputBinding::new(rhs.binding(), dtype_to_storage_type(lhs_dtype)),
163 ),
164 Some((data, scale)) => {
165 if matches!(
167 strategy,
168 Strategy::MultiLevel(cubek::matmul::multi_level::Strategy::Naive)
169 ) && rhs.scheme().block_size().is_some()
170 {
171 rhs = dequantize(rhs.clone(), lhs_dtype);
172 let rhs_dtype = rhs.dtype;
173 (
174 lhs_dtype,
175 InputBinding::new(rhs.binding(), dtype_to_storage_type(rhs_dtype)),
176 )
177 } else {
178 let scheme = rhs.scheme();
179 let data_dtype = data.dtype;
180 let scale_dtype = scale.dtype;
181 (
182 out_dtype,
183 InputBinding::quantized(
184 data.binding(),
185 scale.binding(),
186 rhs.meta.shape().clone(),
187 scheme,
188 dtype_to_storage_type(data_dtype),
189 dtype_to_storage_type(scale_dtype),
190 ),
191 )
192 }
193 }
194 };
195
196 let mut dtypes = MatmulElems::from_globals(&MatmulGlobalElems {
197 lhs: dtype_to_storage_type(lhs_dtype),
198 rhs: dtype_to_storage_type(rhs_dtype),
199 out: dtype_to_storage_type(out_dtype),
200 });
201
202 cubek::matmul::launch::launch_ref(
203 &strategy,
204 client,
205 lhs_handle,
206 rhs_handle,
207 out.clone().binding(),
208 &mut dtypes,
209 )?;
210
211 Ok(())
212}