use super::base::*;
use crate::*;
const M: Axis = Axis(0);
const N: Axis = Axis(1);
const K: Axis = Axis(2);
#[test]
fn a_written_buffer_reads_back_as_its_statement() {
let statements = [
StorageLevels::new(&[(N, 32), (K, 16)]).grid(&[N, K]),
StorageLevels::new(&[(N, 16), (K, 32)]).grid(&[K, N]),
StorageLevels::new(&[(N, 4), (N, 8)])
.tile(&[(K, 2), (K, 16)])
.grid(&[K, N]),
StorageLevels::new(&[(N, 4)])
.tile(&[(N, 1), (K, 2)])
.grid(&[N, K]),
];
for storage in statements {
let geometry = storage.physical(&[(K, 64), (N, 64)]).unwrap();
let read = StoragePartitioning::new(&geometry, &geometry.labels(&[K, N])).unwrap();
assert_eq!(read, storage);
}
}
#[test]
fn an_untiled_buffer_is_its_order_alone() {
let rows = Geometry::new(&[(64, 4096), (4096, 1)]);
let read = StoragePartitioning::new(&rows, &[K, N]).unwrap();
assert!(read.tiles().is_empty());
assert_eq!(read.order(), &[N, K]);
let transposed = Geometry::new(&[(64, 1), (4096, 64)]);
let read = StoragePartitioning::new(&transposed, &[K, N]).unwrap();
assert_eq!(read.order(), &[K, N]);
let broadcast = Geometry::new(&[(3, 0), (1, 5), (4096, 1)]);
let read = StoragePartitioning::new(&broadcast, &[M, K, N]).unwrap();
assert_eq!(read.order(), &[N]);
}
#[test]
fn a_stored_read_and_its_whole_tile_are_both_held() {
let with_read = StorageLevels::new(&[(N, 4)])
.tile(&[(N, 8), (K, 32)])
.grid(&[N, K]);
let extents = [(K, 64), (N, 64)];
assert_eq!(with_read.holds(&[(N, 4)], &extents), Ok(()));
assert_eq!(with_read.holds(&[(N, 32), (K, 32)], &extents), Ok(()));
}
#[test]
fn a_stated_tile_is_never_split() {
let extents = [(K, 64), (N, 64)];
let tiles = StorageLevels::new(&[(N, 32), (K, 32)]).grid(&[N, K]);
assert_eq!(
tiles.holds(&[(N, 4)], &extents),
Err(TileMisfit::Overshoots {
axis: N,
wanted: 4,
piece: 32
})
);
let column_first = StorageLevels::new(&[(K, 32), (N, 32)]).grid(&[N, K]);
assert_eq!(
column_first.holds(&[(N, 32), (K, 32)], &extents),
Err(TileMisfit::Interleaved {
wanted: N,
found: K
})
);
}
#[test]
fn a_read_cuts_itself_out_of_the_rest_of_an_axis() {
let rows = StorageLevels::new(&[]).grid(&[N, K]);
let extents = [(K, 64), (N, 4096)];
assert_eq!(rows.holds(&[(N, 8)], &extents), Ok(()));
assert!(rows.holds(&[(N, 3)], &extents).is_err());
}
#[test]
fn a_word_and_a_two_dimensional_read_are_whole_tiles() {
let values = StorageLevels::new(&[(K, 8)])
.tile(&[(K, 2), (N, 2)])
.tile(&[(K, 8), (N, 8)])
.grid(&[K, N]);
let extents = [(K, 256), (N, 64)];
assert_eq!(values.holds(&[(K, 8)], &extents), Ok(()));
assert_eq!(values.holds(&[(K, 16), (N, 2)], &extents), Ok(()));
assert!(values.holds(&[(K, 32)], &extents).is_err());
}
#[test]
fn a_k_first_grid_moves_the_strides_not_the_dims() {
let storage = StorageLevels::new(&[(N, 16), (K, 32)]).grid(&[K, N]);
let geometry = storage.physical(&[(K, 128), (N, 64)]).unwrap();
assert_eq!(
geometry,
Geometry::new(&[(4, 512), (4, 2048), (32, 16), (16, 1)])
.with_tiling(cubecl::zspace::Tiling::new(&[2, 2]).unwrap())
);
}
#[test]
fn a_statement_that_does_not_fit_the_tensor_is_refused_when_written() {
let eight_rows = StorageLevels::new(&[(K, 8)]).grid(&[K, N]);
assert_eq!(
eight_rows.physical(&[(K, 12), (N, 4)]),
Err(StorageMisfit::PartialTile {
axis: K,
extent: 12,
tile: 8
})
);
let one_axis = StorageLevels::new(&[(K, 4)]).grid(&[K]);
assert!(matches!(
one_axis.physical(&[(K, 12), (N, 4)]),
Err(StorageMisfit::Order { .. })
));
}
#[test]
fn a_piece_of_one_is_a_dim_kept_as_stated() {
let with_one = StorageLevels::new(&[(N, 4)])
.tile(&[(N, 1), (K, 2)])
.grid(&[N, K]);
let without = StorageLevels::new(&[(N, 4), (K, 2)]).grid(&[N, K]);
assert_ne!(with_one, without);
assert_eq!(with_one.labels(&[K, N]), vec![K, N, K, N, N]);
assert_eq!(without.labels(&[K, N]), vec![K, N, K, N]);
let (read, extents) = ([(N, 4), (K, 2)], [(K, 8), (N, 8)]);
assert_eq!(with_one.holds(&read, &extents), Ok(()));
assert_eq!(without.holds(&read, &extents), Ok(()));
}
#[test]
fn labels_name_the_dims_level_major_coarsest_first() {
let untiled = StorageLevels::new(&[]).grid(&[N, K]);
assert_eq!(untiled.labels(&[K, N]), vec![K, N]);
let one_level = StorageLevels::new(&[(N, 32), (K, 32)]).grid(&[N, K]);
assert_eq!(one_level.labels(&[K, N]), vec![K, N, K, N]);
let deeper_n = StorageLevels::new(&[(N, 4), (N, 8), (K, 32)]).grid(&[N, K]);
assert_eq!(deeper_n.labels(&[K, N]), vec![K, N, K, N, N]);
let batched = StorageLevels::new(&[(N, 32), (K, 32)]).grid(&[N, K, M]);
assert_eq!(batched.labels(&[M, K, N]), vec![M, K, N, K, N]);
}
#[test]
fn a_window_is_one_run_inside_the_tiles_whose_axes_do_not_interleave() {
let with_read = StorageLevels::new(&[(N, 4)])
.tile(&[(N, 8), (K, 32)])
.grid(&[N, K]);
assert_eq!(
with_read.contiguous_tiles(&[(K, 64), (N, 64)]),
vec![vec![(N, 4)], vec![(N, 32)], vec![(N, 32), (K, 32)]]
);
let down_k = StorageLevels::new(&[(N, 16), (K, 32)]).grid(&[K, N]);
assert_eq!(
down_k.contiguous_tiles(&[(K, 128), (N, 64)]),
vec![
vec![(N, 16)],
vec![(N, 16), (K, 32)],
vec![(N, 16), (K, 128)]
]
);
let interleaved = StorageLevels::new(&[(N, 16), (K, 2)])
.tile(&[(N, 2), (K, 16)])
.grid(&[N, K]);
assert_eq!(
interleaved.contiguous_tiles(&[(K, 64), (N, 64)]),
vec![vec![(N, 16)], vec![(N, 16), (K, 2)]]
);
}