burn_cubecl_fusion/
base.rs1use burn_fusion::stream::Context;
2use burn_std::{
3 DType, Metadata, Shape, Strides,
4 quantization::{QParamTensor, global_scale_dtype},
5 strides,
6};
7use cubecl::quant::scheme::{QuantScheme, ScaleDtype};
8use cubecl::{
9 client::Client,
10 ir::AddressType,
11 prelude::{TensorArg, TensorBinding},
12 zspace::Tiling,
13};
14
15pub trait FallbackOperation: Send + Sync {
17 fn run(&self, context: &mut Context<CubeFusionHandle>);
19}
20
21pub type QParams = burn_std::quantization::QParams<QParamTensor>;
24
25pub struct CubeFusionHandle {
27 pub client: Client,
29 pub handle: cubecl::server::Handle,
31 pub device: cubecl::Device,
33 pub dtype: DType,
35 pub strides: Strides,
37 pub tiles: Option<Tiles>,
42 pub qparams: Option<QParams>,
44}
45
46#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct Tiles {
50 pub shape: Shape,
52 pub tiling: Tiling,
54}
55
56impl core::fmt::Debug for CubeFusionHandle {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.write_fmt(format_args!(
59 "CubeFusionHandle {{ device: {:?}, runtime: {}}}",
60 self.device,
61 self.client.name(),
62 ))
63 }
64}
65
66impl Clone for CubeFusionHandle {
67 fn clone(&self) -> Self {
68 Self {
69 client: self.client.clone(),
70 handle: self.handle.clone(),
71 device: self.device.clone(),
72 strides: self.strides.clone(),
73 tiles: self.tiles.clone(),
74 dtype: self.dtype,
75 qparams: self.qparams.clone(),
76 }
77 }
78}
79
80unsafe impl Send for CubeFusionHandle {}
81unsafe impl Sync for CubeFusionHandle {}
82
83impl CubeFusionHandle {
84 pub fn binding(self, shape: Shape) -> TensorBinding {
86 let (shape, tiling) = (self.physical_shape(shape), self.tiling());
87 TensorBinding {
88 handle: self.handle.binding(),
89 strides: self.strides,
90 shape,
91 tiling,
92 }
93 }
94
95 pub fn physical_shape(&self, logical: Shape) -> Shape {
98 match &self.tiles {
99 Some(tiles) => tiles.shape.clone(),
100 None => logical,
101 }
102 }
103
104 pub fn metadata(&self, logical: Shape) -> Metadata {
107 match &self.tiles {
108 Some(tiles) => Metadata::new(tiles.shape.clone(), self.strides.clone())
109 .with_tiling(tiles.tiling)
110 .expect("a fusion handle's tiling describes its own rank"),
111 None => Metadata::new(logical, self.strides.clone()),
112 }
113 }
114
115 pub fn tiling(&self) -> Tiling {
117 self.tiles
118 .as_ref()
119 .map(|tiles| tiles.tiling)
120 .unwrap_or(Tiling::UNTILED)
121 }
122
123 pub fn required_address_type(&self) -> AddressType {
124 match self.dtype {
125 DType::QFloat(scheme) => {
126 let len = self.handle.size() as usize * 8 / scheme.size_bits_value();
127 AddressType::from_len(len)
128 }
129 _ => AddressType::from_len(self.handle.size() as usize / self.dtype.size()),
130 }
131 }
132
133 pub fn into_tensor_arg(self, shape: Shape) -> TensorArg {
135 let handle = self.binding(shape);
136 handle.into_tensor_arg()
137 }
138
139 pub fn params(&self, scheme: QuantScheme) -> Option<Self> {
141 let qparams = self.qparams.as_ref()?;
142 assert!(
145 global_scale_dtype(&scheme).is_none(),
146 "fused kernels don't yet support a two-level scheme's per-tensor scale"
147 );
148 let mut handle = self.handle.clone();
149 handle.offset_start = Some(qparams.scales.offset_start as u64);
150 handle.offset_end = Some(qparams.scales.offset_end as u64);
151
152 Some(Self {
153 client: self.client.clone(),
154 handle,
155 device: self.device.clone(),
156 dtype: match scheme.scale_dtype() {
157 ScaleDtype::F32 => DType::F32,
158 ScaleDtype::F16 => DType::F16,
159 ScaleDtype::BF16 => DType::BF16,
160 ScaleDtype::UE8M0 | ScaleDtype::UE4M3 => unimplemented!("Not yet supported"),
161 },
162 strides: qparams.scales.metadata.strides().clone(),
163 tiles: None,
164 qparams: None,
165 })
166 }
167}
168
169pub(crate) fn strides_dyn_rank(shape: &[usize]) -> Strides {
170 let mut strides = strides![0; shape.len()];
171
172 let mut current = 1;
173 shape.iter().enumerate().rev().for_each(|(index, val)| {
174 strides[index] = current;
175 current *= val;
176 });
177
178 strides
179}