Skip to main content

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}