cubecl_common/
profile.rs

1use alloc::boxed::Box;
2use core::fmt::Display;
3
4#[cfg(not(target_os = "none"))]
5pub use web_time::{Duration, Instant};
6
7#[cfg(target_os = "none")]
8pub use embassy_time::{Duration, Instant};
9
10use crate::future::DynFut;
11
12/// How a benchmark's execution times are measured.
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[derive(Debug, Clone, Copy, Eq, PartialEq)]
15pub enum TimingMethod {
16    /// Time measurements come from full timing of execution + sync
17    /// calls.
18    System,
19    /// Time measurements come from hardware reported timestamps
20    /// coming from a sync call.
21    Device,
22}
23
24impl Display for TimingMethod {
25    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
26        match self {
27            TimingMethod::System => f.write_str("system"),
28            TimingMethod::Device => f.write_str("device"),
29        }
30    }
31}
32
33/// Start and end point for a profile. Can be turned into a duration.
34#[derive(Debug)]
35pub struct ProfileTicks {
36    start: Instant,
37    end: Instant,
38}
39
40impl ProfileTicks {
41    /// Create a new `ProfileTicks` from a start and end time.
42    pub fn from_start_end(start: Instant, end: Instant) -> Self {
43        Self { start, end }
44    }
45
46    /// Get the duration contained in this `ProfileTicks`.
47    pub fn duration(&self) -> Duration {
48        self.end.duration_since(self.start)
49    }
50
51    /// Get the duration since the epoch start of this `ProfileTicks`.
52    pub fn start_duration_since(&self, epoch: Instant) -> Duration {
53        self.start.duration_since(epoch)
54    }
55
56    /// Get the duration since the epoch end of this `ProfileTicks`.
57    pub fn end_duration_since(&self, epoch: Instant) -> Duration {
58        self.end.duration_since(epoch)
59    }
60}
61
62/// Result from profiling between two measurements. This can either be a duration or a future that resolves to a duration.
63pub struct ProfileDuration {
64    // The future to read profiling data. For System profiling,
65    // this should be entirely synchronous.
66    future: DynFut<ProfileTicks>,
67    method: TimingMethod,
68}
69
70impl ProfileDuration {
71    /// The method used to measure the execution time.
72    pub fn timing_method(&self) -> TimingMethod {
73        self.method
74    }
75
76    /// Create a new `ProfileDuration` from a future that resolves to a duration.
77    pub fn new(future: DynFut<ProfileTicks>, method: TimingMethod) -> ProfileDuration {
78        Self { future, method }
79    }
80
81    /// Create a new `ProfileDuration` straight from a duration.
82    pub fn new_system_time(start: Instant, end: Instant) -> Self {
83        Self::new(
84            Box::pin(async move { ProfileTicks::from_start_end(start, end) }),
85            TimingMethod::System,
86        )
87    }
88
89    /// Create a new `ProfileDuration` from a future that resolves to a duration.
90    pub fn new_device_time(
91        future: impl Future<Output = ProfileTicks> + Send + 'static,
92    ) -> ProfileDuration {
93        Self::new(Box::pin(future), TimingMethod::Device)
94    }
95
96    /// Retrieve the future that resolves the profile.
97    pub fn into_future(self) -> DynFut<ProfileTicks> {
98        self.future
99    }
100
101    /// Resolve the actual duration of the profile, possibly by waiting for the future to complete.
102    pub async fn resolve(self) -> ProfileTicks {
103        self.future.await
104    }
105}