fuel_cpu_backend/
quantized.rs1use crate::CpuStorage;
12use crate::dyn_impl::CpuBackendDevice;
13use fuel_backend_contract::dyn_backend::DynBackendStorage;
14use fuel_backend_contract::quantized::{DynQuantizedStorage, QuantizedDeviceKernels};
15use fuel_ir::quantized::GgmlDType;
16use fuel_ir::{DType, Error, HostBuffer, Layout, Result, Shape};
17use fuel_quantized::{QuantizedType, cpu_from_data, cpu_zeros};
18use half::f16;
19use std::any::Any;
20use std::borrow::Cow;
21
22pub struct CpuQStorage(pub Box<dyn QuantizedType>);
23
24impl std::fmt::Debug for CpuQStorage {
25 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
26 write!(f, "CpuQStorage[{:?}]", self.0.dtype())
27 }
28}
29
30fn cpu_src_as_slice(src: &dyn DynBackendStorage) -> Result<&[f32]> {
31 let cpu = src
32 .as_any()
33 .downcast_ref::<CpuStorage>()
34 .ok_or_else(|| Error::Msg("expected cpu storage for quantize".into()).bt())?;
35 cpu.0.as_slice::<f32>()
36}
37
38impl DynQuantizedStorage for CpuQStorage {
39 fn dtype(&self) -> GgmlDType {
40 self.0.dtype()
41 }
42 fn block_size(&self) -> usize {
43 self.0.block_size()
44 }
45 fn storage_size_in_bytes(&self) -> usize {
46 self.0.storage_size_in_bytes()
47 }
48 fn quantize(&mut self, src: &dyn DynBackendStorage) -> Result<()> {
49 self.0.dtype().check_quantize()?;
50 self.0.from_float(cpu_src_as_slice(src)?);
51 Ok(())
52 }
53 fn quantize_imatrix(
54 &mut self,
55 src: &dyn DynBackendStorage,
56 imatrix_weights: &[f32],
57 n_per_row: usize,
58 ) -> Result<()> {
59 self.0.dtype().check_quantize()?;
60 self.0
61 .from_float_imatrix(cpu_src_as_slice(src)?, imatrix_weights, n_per_row);
62 Ok(())
63 }
64 fn quantize_onto(&mut self, src: &dyn DynBackendStorage) -> Result<()> {
65 self.0.dtype().check_quantize()?;
66 self.0.from_float(cpu_src_as_slice(src)?);
67 Ok(())
68 }
69 fn quantize_imatrix_onto(
70 &mut self,
71 src: &dyn DynBackendStorage,
72 imatrix_weights: &[f32],
73 n_per_row: usize,
74 ) -> Result<()> {
75 self.0.dtype().check_quantize()?;
76 self.0
77 .from_float_imatrix(cpu_src_as_slice(src)?, imatrix_weights, n_per_row);
78 Ok(())
79 }
80 fn dequantize(&self, elem_count: usize) -> Result<Box<dyn DynBackendStorage>> {
81 let buf = self.0.dequantize(elem_count)?;
82 Ok(Box::new(CpuStorage(buf)))
83 }
84 fn data(&self) -> Result<Cow<'_, [u8]>> {
85 let data_ptr = self.0.as_ptr();
86 let size_in_bytes = self.0.storage_size_in_bytes();
87 let data = unsafe { std::slice::from_raw_parts(data_ptr, size_in_bytes) };
88 Ok(Cow::from(data))
89 }
90 fn fwd(
91 &self,
92 self_shape: &Shape,
93 input: &dyn DynBackendStorage,
94 layout: &Layout,
95 ) -> Result<(Box<dyn DynBackendStorage>, Shape)> {
96 let cpu = input
97 .as_any()
98 .downcast_ref::<CpuStorage>()
99 .ok_or_else(|| Error::Msg("qmatmul: expected cpu storage".into()).bt())?;
100 let storage = &cpu.0;
101 if !layout.is_contiguous() {
102 return Err(Error::Msg(format!("input tensor is not contiguous {layout:?}")).bt());
103 }
104 let src_shape = layout.shape();
105 let (n, k) = self_shape.dims2()?;
106 if src_shape.rank() < 2 {
107 return Err(Error::Msg(format!("input tensor has only one dimension {layout:?}")).bt());
108 }
109 let mut dst_shape = src_shape.dims().to_vec();
110 let last_k = dst_shape.pop().unwrap();
111 if last_k != k {
112 return Err(Error::Msg(format!(
113 "input tensor {layout:?} incompatible with {self_shape:?}"
114 ))
115 .bt());
116 }
117 dst_shape.push(n);
118 let dst_shape = Shape::from(dst_shape);
119 let out = match storage.dtype() {
120 DType::F32 => {
121 let slice = storage.as_slice::<f32>()?;
122 let slice =
123 &slice[layout.start_offset()..layout.start_offset() + src_shape.elem_count()];
124 let mut dst_storage = vec![0f32; dst_shape.elem_count()];
125 self.0
126 .matmul_t((dst_shape.elem_count() / n, k, n), slice, &mut dst_storage)?;
127 HostBuffer::F32(dst_storage)
128 }
129 DType::F16 => {
130 let slice = storage.as_slice::<f16>()?;
131 let slice =
132 &slice[layout.start_offset()..layout.start_offset() + src_shape.elem_count()];
133 let mut dst_storage = vec![f16::ZERO; dst_shape.elem_count()];
134 self.0
135 .matmul_t_f16((dst_shape.elem_count() / n, k, n), slice, &mut dst_storage)?;
136 HostBuffer::F16(dst_storage)
137 }
138 _ => return Err(Error::Msg("Expected f32/f16".into()).bt()),
139 };
140 Ok((Box::new(CpuStorage(out)), dst_shape))
141 }
142 fn as_any(&self) -> &dyn Any {
143 self
144 }
145 fn device_arc_dyn(
146 &self,
147 ) -> std::sync::Arc<dyn fuel_backend_contract::dyn_backend::DynBackendDevice> {
148 std::sync::Arc::new(CpuBackendDevice)
149 }
150}
151
152impl QuantizedDeviceKernels for CpuBackendDevice {
153 fn qzeros(&self, elem_count: usize, dtype: GgmlDType) -> Result<Box<dyn DynQuantizedStorage>> {
154 Ok(Box::new(CpuQStorage(cpu_zeros(dtype, elem_count))))
155 }
156 fn load_quantized(
157 &self,
158 dtype: GgmlDType,
159 data: Cow<'_, [u8]>,
160 ) -> Result<Box<dyn DynQuantizedStorage>> {
161 Ok(Box::new(CpuQStorage(cpu_from_data(dtype, data)?)))
162 }
163}