1use downcast_rs::{Downcast, impl_downcast};
2use dyn_clone::DynClone;
3use dyn_eq::DynEq;
4use dyn_hash::DynHash;
5use std::alloc::Layout;
6use std::fmt::{Debug, Display};
7use std::hash::Hash;
8use std::sync::Arc;
9use tract_data::internal::*;
10
11use crate::WeightType;
12
13pub trait MMMInputFormat:
14 Downcast + Debug + DynHash + dyn_eq::DynEq + DynClone + Send + Sync + Display
15{
16 fn prepare_tensor(&self, t: &Tensor, k_axis: usize, mn_axis: usize) -> TractResult<Tensor>;
17 fn prepare_one_view(
21 &self,
22 t: &TensorView,
23 k_axis: usize,
24 mn_axis: usize,
25 ) -> TractResult<Box<dyn MMMInputValue>>;
26 fn prepare_one(
27 &self,
28 t: &Tensor,
29 k_axis: usize,
30 mn_axis: usize,
31 ) -> TractResult<Box<dyn MMMInputValue>> {
32 self.prepare_one_view(&t.view(), k_axis, mn_axis)
33 }
34 fn precursor(&self) -> WeightType;
35 fn simulate_precision_loss(&self, tensor: Tensor) -> TractResult<Tensor> {
39 Ok(tensor)
40 }
41 fn r(&self) -> usize;
42 fn k_alignment(&self) -> usize;
43 fn merge_with<'o, 'a: 'o, 'b: 'o>(
44 &'a self,
45 other: &'b dyn MMMInputFormat,
46 ) -> Option<&'o dyn MMMInputFormat> {
47 if self.dyn_eq(other) { Some(other) } else { None }
48 }
49 fn mem_size(&self, k: TDim, mn: TDim) -> TDim;
50 fn extract_at_mn_f16(
51 &self,
52 data: &EagerPackedInput,
53 mn: usize,
54 slice: &mut [f16],
55 ) -> TractResult<()>;
56 fn extract_at_mn_f32(
57 &self,
58 data: &EagerPackedInput,
59 mn: usize,
60 slice: &mut [f32],
61 ) -> TractResult<()>;
62}
63
64dyn_clone::clone_trait_object!(MMMInputFormat);
65impl_downcast!(MMMInputFormat);
66dyn_hash::hash_trait_object!(MMMInputFormat);
67dyn_eq::eq_trait_object!(MMMInputFormat);
68
69pub trait MMMInputValue:
70 DynClone + Debug + DynHash + dyn_eq::DynEq + Send + Sync + Display + Downcast
71{
72 fn format(&self) -> &dyn MMMInputFormat;
73 fn scratch_panel_buffer_layout(&self) -> Option<Layout>;
74 fn panel_bytes(&self, i: usize, buffer: Option<*mut u8>) -> TractResult<*const u8>;
75 fn panels_count(&self) -> usize {
76 self.mn().divceil(self.format().r())
77 }
78 fn mn(&self) -> usize;
79 fn k(&self) -> usize;
80 fn exotic_fact(&self) -> &dyn ExoticFact;
81
82 fn extract_at_mn_f16(&self, mn: usize, slice: &mut [f16]) -> TractResult<()>;
83 fn extract_at_mn_f32(&self, mn: usize, slice: &mut [f32]) -> TractResult<()>;
84}
85dyn_clone::clone_trait_object!(MMMInputValue);
86impl_downcast!(MMMInputValue);
87dyn_hash::hash_trait_object!(MMMInputValue);
88dyn_eq::eq_trait_object!(MMMInputValue);
89
90#[allow(clippy::derived_hash_with_manual_eq)]
91#[derive(Clone, Hash, Debug)]
92pub struct PackedExoticFact {
93 pub format: Box<dyn MMMInputFormat>,
94 pub mn: TDim,
95 pub k: usize,
96}
97
98impl Display for PackedExoticFact {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 write!(f, "Eager {} tensor (mn={} k={})", self.format, self.mn, self.k)
101 }
102}
103
104impl ExoticFact for PackedExoticFact {
105 fn buffer_sizes(&self) -> TVec<TDim> {
106 tvec!(self.format.mem_size(self.k.to_dim(), self.mn.clone()))
107 }
108}
109
110impl PartialEq for PackedExoticFact {
111 fn eq(&self, other: &Self) -> bool {
112 self.format == other.format && self.mn == other.mn && self.k == other.k
113 }
114}
115impl Eq for PackedExoticFact {}
116
117#[derive(Clone, Hash, PartialEq, Eq)]
118pub struct EagerPackedInput {
119 pub fact: PackedExoticFact,
120 pub packed: Arc<Blob>,
121 pub panel_bytes: usize,
122 pub mn: usize,
123}
124
125impl MMMInputValue for EagerPackedInput {
126 fn scratch_panel_buffer_layout(&self) -> Option<Layout> {
127 None
128 }
129 fn panel_bytes(&self, i: usize, _buffer: Option<*mut u8>) -> TractResult<*const u8> {
130 unsafe { Ok(self.packed.as_ptr().add(i * self.panel_bytes)) }
131 }
132 fn k(&self) -> usize {
133 self.fact.k
134 }
135 fn mn(&self) -> usize {
136 self.mn
137 }
138 fn format(&self) -> &dyn MMMInputFormat {
139 &*self.fact.format
140 }
141 fn exotic_fact(&self) -> &dyn ExoticFact {
142 &self.fact
143 }
144 fn extract_at_mn_f16(&self, mn: usize, slice: &mut [f16]) -> TractResult<()> {
145 ensure!(slice.len() == self.k());
146 ensure!(mn < self.mn());
147 self.fact.format.extract_at_mn_f16(self, mn, slice)
148 }
149 fn extract_at_mn_f32(&self, mn: usize, slice: &mut [f32]) -> TractResult<()> {
150 ensure!(slice.len() == self.k());
151 ensure!(mn < self.mn());
152 self.fact.format.extract_at_mn_f32(self, mn, slice)
153 }
154}
155
156impl Display for EagerPackedInput {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 (&self.fact as &dyn Display).fmt(f)
159 }
160}
161
162impl Debug for EagerPackedInput {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 <Self as Display>::fmt(self, f)
165 }
166}
167
168#[cfg(test)]
169pub mod view_check {
170 use super::*;
171
172 pub fn view_matches_tensor(
176 format: &dyn MMMInputFormat,
177 k: usize,
178 mn: usize,
179 ) -> TractResult<()> {
180 let WeightType::Plain(dt) = format.precursor() else {
181 bail!("{format} does not pack a plain tensor")
182 };
183 let mut parent = Tensor::zero_dt(dt, &[2, k, mn])?;
184 parent.as_bytes_mut().iter_mut().enumerate().for_each(|(ix, b)| *b = (ix % 101) as u8 + 1);
185 let batch = parent.slice(0, 1, 2)?.into_shape(&[k, mn])?;
186 let from_tensor = format.prepare_one(&batch, 0, 1)?;
187 let offset = parent.strides()[0] * dt.size_of() as isize;
188 let view =
189 unsafe { TensorView::from_bytes(&parent, offset, parent.shape(), parent.strides()) };
190 let from_view = format.prepare_one_view(&view, 1, 2)?;
191 let bytes = |v: &dyn MMMInputValue| -> TractResult<Arc<Blob>> {
192 Ok(v.downcast_ref::<EagerPackedInput>()
193 .with_context(|| format!("{format} did not pack to an EagerPackedInput"))?
194 .packed
195 .clone())
196 };
197 ensure!(
198 *bytes(&*from_tensor)? == *bytes(&*from_view)?,
199 "{format}: packing k={k} mn={mn} from a view differs from packing it from a tensor"
200 );
201 Ok(())
202 }
203}