#![cfg(all(target_os = "macos", not(feature = "spirv")))]
use cubecl_core as cubecl;
use cubecl_core::prelude::*;
use cubecl_core::profile::TimingMethod;
use cubecl_core::server::Handle;
use cubecl_environment::future::block_on;
use cubecl_server::runtime::Runtime;
use cubecl_wgpu::WgpuRuntime;
use std::time::Duration;
const READS_PER_UNIT: usize = 64;
const CUBE_UNITS: u32 = 256;
#[cube(launch_unchecked)]
fn read_through(buffer: &mut [u32], units: u32) {
let units = usize::cast_from(units);
if ABSOLUTE_POS < units {
let mut sum = 0u32;
for i in 0..READS_PER_UNIT {
sum += buffer[ABSOLUTE_POS + i * units];
}
buffer[READS_PER_UNIT * units + ABSOLUTE_POS] = sum;
}
}
struct Read {
buffer: Handle,
units: usize,
}
impl Read {
fn new(client: &Client, units: usize) -> Self {
Self {
buffer: client.empty((READS_PER_UNIT + 1) * units * size_of::<u32>()),
units,
}
}
fn launch(&self, client: &Client) {
unsafe {
read_through::launch_unchecked(
client,
CubeCount::Static((self.units as u32).div_ceil(CUBE_UNITS), 1, 1),
CubeDim::new_1d(CUBE_UNITS),
BufferArg::from_raw_parts(self.buffer.clone(), (READS_PER_UNIT + 1) * self.units),
self.units as u32,
);
}
}
fn profiled(&self, client: &Client) -> Duration {
let (_, duration) = client
.profile(|| self.launch(client), "read_through")
.expect("the window opens and closes");
block_on(duration.resolve())
.expect("the window resolves a device timing")
.duration()
}
}
fn median(mut samples: Vec<Duration>) -> Duration {
samples.sort();
samples[samples.len() / 2]
}
#[test]
fn a_window_does_not_time_the_work_queued_ahead_of_it() {
let client = <WgpuRuntime>::client(&Default::default());
assert_eq!(
client.properties().timing_method,
TimingMethod::Device,
"the device timer is what can see the work ahead of a window; the host's drains it"
);
let ahead = Read::new(&client, 1 << 20);
let sample = Read::new(&client, 1 << 17);
ahead.launch(&client);
sample.profiled(&client);
let mut alone = Vec::new();
let mut queued = Vec::new();
for _ in 0..7 {
block_on(client.sync()).expect("the device runs");
alone.push(sample.profiled(&client));
for _ in 0..4 {
ahead.launch(&client);
}
queued.push(sample.profiled(&client));
}
let (alone, queued) = (median(alone), median(queued));
eprintln!("alone {alone:?}, behind queued work {queued:?}");
assert!(
queued.as_secs_f64() < 1.3 * alone.as_secs_f64(),
"a window opened behind queued work timed {queued:?}, against {alone:?} on an idle device"
);
}