use super::super::PlanError;
pub(crate) const HIDDEN: usize = 128;
pub(crate) const GATES: usize = 4;
pub(crate) const GATE_COLUMNS: usize = GATES * HIDDEN;
pub(crate) const GROUPS: usize = 32;
pub(crate) const TILE_ROWS: usize = 32;
pub(crate) const STATE_TILE: usize = 2 * TILE_ROWS * HIDDEN;
pub(crate) const ONNX_GATE: [usize; GATES] = [0, 2, 3, 1];
pub(crate) const KERNEL: &str = "spk_lstm_recurrence";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Schedule {
pub(crate) tile_rows: usize,
pub(crate) tiles: usize,
pub(crate) tiles_per_launch: usize,
pub(crate) concurrent: bool,
}
impl Schedule {
const TILE_HEIGHTS: [usize; 3] = [8, 16, TILE_ROWS];
pub(crate) fn new(
batch: usize,
capacity: usize,
concurrent_capacity: Option<usize>,
) -> Result<Self, PlanError> {
Self::for_groups(GROUPS, batch, capacity, concurrent_capacity)
}
pub(crate) fn for_groups(
groups: usize,
batch: usize,
capacity: usize,
concurrent_capacity: Option<usize>,
) -> Result<Self, PlanError> {
let pair = 2 * groups;
let joint = concurrent_capacity.unwrap_or(0).min(capacity);
for tile_rows in Self::TILE_HEIGHTS {
let tiles = batch.div_ceil(tile_rows);
if tiles * pair <= joint {
return Ok(Self {
tile_rows,
tiles,
tiles_per_launch: tiles,
concurrent: true,
});
}
}
let tiles = batch.div_ceil(TILE_ROWS);
if groups <= capacity {
return Ok(Self {
tile_rows: TILE_ROWS,
tiles,
tiles_per_launch: capacity / groups,
concurrent: false,
});
}
Err(PlanError::DeviceUnsupported {
reason: format!(
"needs {groups} co-resident blocks, but the GPU holds {capacity} in a cooperative launch"
),
})
}
pub(crate) fn launches(self) -> impl Iterator<Item = (usize, usize)> {
(0..self.tiles)
.step_by(self.tiles_per_launch)
.map(move |first| (first, self.tiles_per_launch.min(self.tiles - first)))
}
}
pub(crate) fn pack_directions(source: &[f32], columns: usize) -> Vec<f32> {
let direction_len = GATE_COLUMNS * columns;
let mut packed = Vec::with_capacity(source.len());
for direction in source.chunks_exact(direction_len) {
for unit in 0..HIDDEN {
for gate in ONNX_GATE {
let row = gate * HIDDEN + unit;
packed.extend_from_slice(&direction[row * columns..(row + 1) * columns]);
}
}
}
packed
}
pub(crate) fn pack_bias(source: &[f32]) -> Vec<f32> {
let mut packed = Vec::with_capacity(source.len() / 2);
for direction in source.as_chunks::<{ 2 * GATE_COLUMNS }>().0 {
let (input, recurrent) = direction.split_at(GATE_COLUMNS);
for unit in 0..HIDDEN {
for gate in ONNX_GATE {
let row = gate * HIDDEN + unit;
packed.push(input[row] + recurrent[row]);
}
}
}
packed
}