1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use cubecl::prelude::*;
use cubecl_core as cubecl;
pub fn build_kernel(
client: &cubecl_runtime::client::Client,
_key: cubecl_runtime::throughput::ThroughputKey,
_config: super::super::LaunchConfig,
) -> cubecl_runtime::throughput::KernelConfig {
let client = client.clone();
let sample = alloc::boxed::Box::new(move |iterations: usize| {
let input = client.empty(core::mem::size_of::<i32>());
let output = client.empty(core::mem::size_of::<i32>());
// Opened before the window and read only if the device times nothing:
// the fallback has to span the same launches, so it starts with them.
let start = cubecl_common::profile::Instant::now();
let profiled = client.profile(
|| unsafe {
for _ in 0..iterations {
launch_overhead::launch_unchecked(
&client,
cubecl_core::CubeCount::new_single(),
cubecl_core::server::CubeDim::new_single(),
1,
cubecl_core::frontend::BufferArg::from_raw_parts(input.clone(), 1),
cubecl_core::frontend::BufferArg::from_raw_parts(output.clone(), 1),
cubecl_core::ir::ElemType::Int(cubecl_core::ir::IntKind::I32),
);
}
},
"launch_overhead",
);
// What the device made of the window, when it made anything of it. Two
// ways it makes nothing, both seen on Metal and neither a broken probe:
//
// * [`ProfileError::NotMeasured`] — the window resolved no timestamped
// pass at all, so there is no span to report.
// * `None` — the pair came back unwritten or out of order, which
// `stop_profile` declines to pass off as a duration.
//
// Any other profile error is a window that did not run as asked, and it
// is not this probe's to interpret either.
let measured = match profiled {
Ok((_, duration)) => {
cubecl_core::future::block_on(duration.into_future()).map(|ticks| ticks.duration())
}
Err(_) => None,
};
// The host clock over the same launches, which is what every other
// probe in this module times with. A launch costs the caller what it
// adds to the wall clock, and `iterations` of them amortize the single
// sync this pays for, so the host span is a measurement of the same
// thing rather than a stand-in for one.
//
// The alternative is to have no number: the benchmarker takes a
// `Duration` and nothing else, so a probe that cannot answer used to
// panic here — on a device where the ceiling is fine and only its timer
// is quiet, taking the autotune thread (and the session on it) with it.
// A window that genuinely did not run reads as ~0, which is exactly the
// `unwrap_or_default` a failed probe already resolves to upstream.
measured.unwrap_or_else(|| {
let _ = cubecl_core::future::block_on(client.sync());
start.elapsed()
})
});
cubecl_runtime::throughput::KernelConfig {
sample,
ops_count: 1,
min_iterations: 1,
}
}
#[cube(launch_unchecked)]
pub fn launch_overhead<I: Numeric, N: Size>(
input: &[Vector<I, N>],
output: &mut [Vector<I, N>],
#[define(I)] _dtype: ElemType,
) {
if ABSOLUTE_POS == 0 {
output[0] = input[0];
}
}