use cubecl::prelude::*;
use cubecl_core as cubecl;
use cubecl_runtime::throughput::{KernelConfig, MemoryAccess, ThroughputKey};
use crate::throughput::{LaunchConfig, memory_probe::MemoryProbe};
pub fn build_kernel<R: Runtime>(
client: &ComputeClient<R>,
key: ThroughputKey,
config: LaunchConfig,
working_set: usize,
) -> KernelConfig {
let client = client.clone();
let dtype = key.dtype();
let line_bytes = config.vector_size * dtype.size();
let probe = MemoryProbe::new(
&client,
config,
line_bytes,
MemoryAccess::Write,
working_set,
);
let out_handle = client.empty(probe.buffer_bytes);
let sample = Box::new(move |iterations: usize| {
let start = cubecl_common::profile::Instant::now();
unsafe {
memory_write_throughput::launch_unchecked(
&client,
CubeCount::Static(probe.cube_count as u32, 1, 1),
CubeDim::new(&client, config.cube_dim),
config.vector_size,
BufferArg::from_raw_parts(out_handle.clone(), probe.pool_lines),
probe.window_lines,
iterations,
probe.blocked,
dtype,
)
};
let _ = cubecl_core::future::block_on(client.sync());
start.elapsed()
});
let ops_count = probe.window_lines * config.vector_size;
KernelConfig { sample, ops_count }
}
#[cube(launch_unchecked)]
pub fn memory_write_throughput<I: Numeric, N: Size>(
output: &mut [Vector<I, N>],
window: usize,
n_iter: usize,
#[comptime] blocked: bool,
#[define(I)] _dtype: ElemType,
) {
let len = output.len();
let stride = CUBE_DIM as usize * CUBE_COUNT;
let steps = window.div_ceil(stride).max(1);
let seed = I::cast_from(n_iter);
let mut line = Vector::<I, N>::empty();
let lanes = line.vector_size();
#[unroll]
for lane in 0..lanes {
line.insert(lane, seed + I::cast_from(lane));
}
let mut start = 0;
let mut wrap = 0;
for _ in 0..n_iter {
for step in 0..steps {
let base = if blocked {
ABSOLUTE_POS * steps + step
} else {
ABSOLUTE_POS + (step * stride)
};
if base < window {
let mut idx = start + base;
if idx >= len {
idx -= len;
}
output[idx] = line;
}
}
start += window;
if start + window > len {
wrap += 1;
if wrap >= window {
wrap = 0;
}
start = wrap;
}
}
}