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