use cubecl::prelude::*;
use crate::launch::arg::adding::adding_tile;
use crate::launch::arg::tensor::TileArgExpand;
use crate::space::Extent;
use crate::*;
#[derive(CubeType)]
pub struct LastArrival<'a> {
slots: Tile,
adding: Tile,
boxes: Walk,
own: Tile,
arrivals: &'a [Atomic<u32>],
region: Region,
counter: usize,
first: usize,
holders: usize,
first_slot: usize,
}
#[cube]
impl<'a> TileArg<'a> {
pub fn last_arrival<'t, V: Size>(
&self,
partitioning: &Partitioning,
portion: &Portion,
i: usize,
arrivals: &'t [Atomic<u32>],
) -> LastArrival<'t> {
let boxes_space = comptime!(partitioning.clone().arrival_slots_space(self.spec.axes()));
let boxes = Partitioning {
space: Space::with_sizes(
comptime!(boxes_space.space().clone()),
partitioning.space.sizes.clone(),
),
levels: comptime!(boxes_space.levels().to_vec()),
};
let (first, holders) = portion.holders(i);
let run = portion.run();
let first_slot = 2 * first + select(portion.starts_in(i, first), 0usize, 1usize);
let slots = self.tile(&boxes);
let define!(Served) = self.element;
let walk = boxes.walk();
let own_box = walk.region(2 * run + select(portion.starts_in(i, run), 0usize, 1usize));
let own = slots.at(&own_box);
let first_turn = false;
LastArrival::<'t> {
slots,
adding: adding_tile::<Served, V>(self, &boxes, first_turn),
boxes: walk,
own,
arrivals,
region: portion.region(i),
counter: portion.index(i),
first,
holders,
first_slot,
}
}
}
#[cube]
impl<'a> LastArrival<'a> {
pub fn tile(&self) -> Tile {
self.own.clone()
}
pub fn hand_on(&self, sum: &Tile, out: &mut Tile) {
let slot = self.own.projection();
let output = out.projection();
comptime!(assert!(
slot.logical_axes() == output.logical_axes(),
"LastArrival::hand_on: the slots span {:?} and the output {:?}; a sum opened on a slot \
drains into the output axis for axis, so the slots span the output's axes in its order",
slot.logical_axes(),
output.logical_axes()
));
if self.holders == 1 {
sum.drained_into(&out.at(&self.region));
} else {
sum.drained_into(&self.tile());
sync_storage();
let mut arrived = Shared::<[u32]>::new_slice(1usize);
if UNIT_POS == 0 {
arrived[0] = self.arrivals[self.counter].fetch_add(1u32);
}
sync_cube();
if arrived[0] as usize + 1 == self.holders {
sync_storage();
let total = self.boxes.region(self.first_slot);
let mut sum_into = self.adding.at(&total);
for run in self.first + 1..self.first + self.holders {
sum_into.copy_from(&self.slots.at(&self.boxes.region(2 * run)));
}
sync_storage();
let mut out_box = out.at(&self.region);
out_box.copy_cast_from(&self.slots.at(&total));
if UNIT_POS == 0 {
self.arrivals[self.counter].store(0u32);
}
}
}
}
}
impl Partitioning {
pub fn arrival_counters(&self) -> usize {
let level = &self.levels()[0];
self.space()
.axes()
.map(|axis| level.tiles(self.space(), axis))
.product()
}
pub fn arrival_slots(&self, axes: &[Axis]) -> cubecl::zspace::Shape {
let boxes = self.arrival_slots_space(axes);
cubecl::zspace::Shape::from(axes.iter().map(|&axis| boxes.space().extent(axis)))
}
fn arrival_slots_space(&self, axes: &[Axis]) -> Partitioning {
let level = &self.levels()[0];
let runs = level.shared_by().expect(
"LastArrival: the cube level distributes no grid as one index; say `shared_by`",
);
let stacked = axes[0];
let cube_box = level.child(self.space());
let Extent::Static(edge) = cube_box.extent_raw(stacked) else {
panic!(
"LastArrival: the cube box's {stacked:?} extent is known only at launch; the slots \
stack cube boxes along it, so the cube level cuts it by a stated tile"
)
};
let extents: Vec<(Axis, Extent)> = cube_box
.axes()
.map(|axis| match axis == stacked {
true => (axis, Extent::Static(2 * runs * edge)),
false => (axis, cube_box.extent_raw(axis)),
})
.collect();
Partitioning::new(Space::from_extents(&extents), self.levels().to_vec())
}
}