use cubecl::{
prelude::*,
std::tensor::layout::{Coordinates, Coords2d},
};
use crate::*;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum InitFrom {
Cell,
Identity,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum CellRead {
AtSeed,
AtCommit,
Never,
}
impl CellRead {
const fn of(unit_share: UnitShare, init_from: InitFrom, write: Write) -> Self {
match write {
Write::Accumulate | Write::Exclusive(_) => CellRead::Never,
Write::Replace => match init_from {
InitFrom::Identity => CellRead::Never,
InitFrom::Cell => match unit_share {
UnitShare::Repeated | UnitShare::Whole => CellRead::AtSeed,
UnitShare::Plane | UnitShare::Group { .. } => CellRead::AtCommit,
},
},
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum DrainPlan {
Straight,
BounceTogether,
BounceEach,
}
impl DrainPlan {
pub(crate) fn new(scratch: Scratch) -> Self {
match scratch {
Scratch::None => DrainPlan::Straight,
Scratch::OneTile => DrainPlan::BounceEach,
Scratch::WholeGrid => DrainPlan::BounceTogether,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum DrainPass {
Copy,
Bounce,
Spill,
Add,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum Drain {
EachUnit,
UnitZero,
PlaneFold,
GroupFold { unit_bits: usize },
}
impl Drain {
pub(crate) const fn of(units: UnitShare, write: Write) -> Self {
match (units, write) {
(UnitShare::Plane, _) => Drain::PlaneFold,
(UnitShare::Group { unit_bits }, _) => Drain::GroupFold { unit_bits },
(UnitShare::Repeated, Write::Accumulate | Write::Exclusive(_)) => Drain::UnitZero,
(UnitShare::Repeated, Write::Replace) | (UnitShare::Whole, _) => Drain::EachUnit,
}
}
}
#[derive(CubeType)]
pub(crate) struct AccumulateView<'a, E: Numeric, V: Size, C: Coordinates + 'a = Coords2d> {
values: MaskedMut<'a, Vector<E, V>, C>,
#[cube(comptime)]
units: UnitShare,
#[cube(comptime)]
monoid: Monoid,
#[cube(comptime)]
cell_read: CellRead,
#[cube(comptime)]
drain: Drain,
}
#[cube]
impl<'a, E: Numeric, V: Size, C: Coordinates + 'a> AccumulateView<'a, E, V, C> {
pub(crate) fn new(
values: MaskedMut<'a, Vector<E, V>, C>,
#[comptime] units: UnitShare,
#[comptime] split_share: SplitShare,
#[comptime] write: Write,
#[comptime] monoid: Monoid,
#[comptime] init_from: InitFrom,
) -> Self {
comptime!(write.admits(split_share, monoid, "AccumulateView"));
AccumulateView::<'a, E, V, C> {
values,
units,
monoid,
cell_read: comptime!(CellRead::of(units, init_from, write)),
drain: comptime!(Drain::of(units, write)),
}
}
pub(crate) fn check(&self) -> comptime_type!(bool) {
comptime!(self.values.check)
}
pub(crate) fn unit_share(&self) -> comptime_type!(UnitShare) {
comptime!(self.units)
}
pub(crate) fn monoid(&self) -> comptime_type!(Monoid) {
comptime!(self.monoid)
}
pub(crate) fn block_in_bounds(&self, pos: C, extent: C) -> bool {
self.values.block_in_bounds(pos, extent)
}
pub(crate) fn seed(&self, pos: C) -> Vector<E, V> {
match comptime!(self.cell_read) {
CellRead::AtSeed => self.values.read(pos),
CellRead::AtCommit | CellRead::Never => {
Vector::<E, V>::cast_from(Monoid::identity::<E>(self.monoid))
}
}
}
pub(crate) fn commit(&mut self, pos: C, value: Vector<E, V>) {
match comptime!(self.drain) {
Drain::PlaneFold => {
let combined = self.units.reduce::<Vector<E, V>>(value, self.monoid);
self.commit_shared(pos, combined, UNIT_POS_X == 0);
}
Drain::GroupFold { unit_bits } => {
let combined = self.units.reduce::<Vector<E, V>>(value, self.monoid);
let unit_in_group = UNIT_POS_X & comptime!(unit_bits as u32);
self.commit_shared(pos, combined, unit_in_group == 0);
}
Drain::UnitZero => self.commit_shared(pos, value, UNIT_POS_X == 0),
Drain::EachUnit => self.values.write(pos, value),
}
}
fn commit_shared(&mut self, pos: C, combined: Vector<E, V>, leader: bool) {
if leader {
match comptime!(self.cell_read) {
CellRead::AtCommit => {
let old = self.values.read(pos.clone());
self.values
.write(pos, self.monoid.combine::<Vector<E, V>>(old, combined));
}
CellRead::AtSeed | CellRead::Never => self.values.write(pos, combined),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_destination_that_adds_is_written_once_and_never_read() {
for write in [Write::Accumulate, Write::Exclusive(Schedule::Sequential)] {
assert_eq!(Drain::of(UnitShare::Repeated, write), Drain::UnitZero);
for init_from in [InitFrom::Cell, InitFrom::Identity] {
assert_eq!(
CellRead::of(UnitShare::Whole, init_from, write),
CellRead::Never
);
}
}
assert_eq!(
Drain::of(UnitShare::Repeated, Write::Replace),
Drain::EachUnit
);
}
}