1use serde::{Deserialize, Serialize};
2use smallvec::SmallVec;
3
4use crate::{
5 INLINE_DIMS, MetadataError,
6 shape::Shape,
7 strides::Strides,
8 tiling::{MAX_FRAGMENTS, Tiling},
9};
10
11#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
12pub struct Metadata {
13 pub shape: Shape,
14 pub strides: Strides,
15 pub tiling: Tiling,
18}
19
20impl Metadata {
21 pub fn new(shape: impl Into<Shape>, strides: impl Into<Strides>) -> Self {
22 let shape = shape.into();
23 let strides = strides.into();
24 debug_assert_eq!(
25 shape.rank(),
26 strides.rank(),
27 "Rank of shape and strides must be the same"
28 );
29
30 Self {
31 shape,
32 strides,
33 tiling: Tiling::UNTILED,
34 }
35 }
36
37 pub fn with_tiling(mut self, tiling: Tiling) -> Result<Self, MetadataError> {
43 tiling.logical_rank(self.rank())?;
44 self.tiling = tiling;
45 Ok(self)
46 }
47
48 pub fn is_tiled(&self) -> bool {
50 self.tiling.is_tiled()
51 }
52
53 pub fn logical_rank(&self) -> Result<usize, MetadataError> {
60 self.tiling.logical_rank(self.rank())
61 }
62
63 pub fn logical_shape(&self) -> Result<Shape, MetadataError> {
71 let fragments = self.tiling.fragments(self.logical_rank()?);
72 let mut extents = SmallVec::<[usize; INLINE_DIMS]>::from_elem(1, fragments.len());
73 let dims = self.shape.as_slice();
77 let mut physical = 0;
78 for level in 0..MAX_FRAGMENTS {
79 for (dim, &count) in fragments.iter().enumerate() {
80 if level < count {
81 extents[dim] *= dims[physical];
82 physical += 1;
83 }
84 }
85 }
86 Ok(Shape::new_raw(extents))
87 }
88
89 fn assert_untiled(&self, op: &str) {
92 assert!(
93 !self.is_tiled(),
94 "Metadata::{op} on a storage-tiled tensor is not supported: {:?}",
95 self.tiling
96 );
97 }
98
99 pub fn shape(&self) -> &Shape {
100 &self.shape
101 }
102
103 pub fn shape_mut(&mut self) -> &mut Shape {
106 self.assert_untiled("shape_mut");
107 &mut self.shape
108 }
109
110 pub fn strides(&self) -> &Strides {
111 &self.strides
112 }
113
114 pub fn strides_mut(&mut self) -> &mut Strides {
118 self.assert_untiled("strides_mut");
119 &mut self.strides
120 }
121
122 pub fn rank(&self) -> usize {
123 self.num_dims()
124 }
125
126 pub fn num_dims(&self) -> usize {
127 self.shape.num_dims()
128 }
129
130 pub fn num_elements(&self) -> usize {
132 self.shape.num_elements()
133 }
134
135 pub fn swapped(mut self, dim0: usize, dim1: usize) -> Self {
136 self.swap(dim0, dim1);
137 self
138 }
139
140 pub fn swap(&mut self, dim0: usize, dim1: usize) {
141 self.assert_untiled("swap");
142 debug_assert!(dim0 < self.rank(), "dim0 is out of bounds");
143 debug_assert!(dim1 < self.rank(), "dim1 is out of bounds");
144 self.shape.swap(dim0, dim1);
145 self.strides.swap(dim0, dim1);
146 }
147
148 pub fn permute(&mut self, axes: &[usize]) -> Result<(), MetadataError> {
150 self.assert_untiled("permute");
151 self.shape.permute(axes)?;
152 self.strides.permute(axes)?;
153
154 Ok(())
155 }
156
157 pub fn permuted(mut self, axes: &[usize]) -> Result<Self, MetadataError> {
158 self.permute(axes)?;
159 Ok(self)
160 }
161
162 pub fn insert(&mut self, index: usize, shape: usize, stride: usize) {
164 self.assert_untiled("insert");
165 self.shape.insert(index, shape);
166 self.strides.insert(index, stride);
167 }
168
169 pub fn remove(&mut self, index: usize) -> (usize, usize) {
171 self.assert_untiled("remove");
172 let shape = self.shape.remove(index);
173 let stride = self.strides.remove(index);
174 (shape, stride)
175 }
176
177 pub fn push(&mut self, shape: usize, stride: usize) {
179 self.assert_untiled("push");
180 self.shape.push(shape);
181 self.strides.push(stride);
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
193 fn logical_shape_multiplies_a_dim_s_fragments_back_together() {
194 let meta = Metadata::new(
195 [2, 128, 344, 32, 32],
196 [128 * 344 * 1024, 344 * 1024, 1024, 32, 1],
197 )
198 .with_tiling(Tiling::new(&[1, 2, 2]).unwrap())
199 .unwrap();
200
201 assert_eq!(meta.logical_rank(), Ok(3));
202 assert_eq!(meta.logical_shape(), Ok(Shape::new([2, 4096, 11008])));
203 }
204
205 #[test]
208 fn an_untiled_metadata_stands_for_its_own_shape() {
209 let meta = Metadata::new([2, 4096, 11008], [4096 * 11008, 11008, 1]);
210
211 assert_eq!(meta.logical_rank(), Ok(3));
212 assert_eq!(meta.logical_shape(), Ok(meta.shape.clone()));
213 }
214
215 #[test]
218 fn dims_tiled_to_different_depths_each_collect_their_own() {
219 let meta = Metadata::new([4, 8, 2, 4, 2], [1; 5])
220 .with_tiling(Tiling::new(&[3, 2]).unwrap())
221 .unwrap();
222
223 assert_eq!(meta.logical_shape(), Ok(Shape::new([4 * 2 * 2, 8 * 4])));
224 }
225
226 #[test]
229 fn a_buffer_too_short_for_the_tiling_is_refused() {
230 let mut meta = Metadata::new([2, 128, 344, 32, 32], [1; 5])
231 .with_tiling(Tiling::new(&[1, 2, 2]).unwrap())
232 .unwrap();
233 meta.shape.remove(4);
234 meta.strides.remove(4);
235 meta.shape.remove(3);
236 meta.strides.remove(3);
237
238 assert!(meta.logical_rank().is_err());
239 assert!(meta.logical_shape().is_err());
240 }
241}