| 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. |
| 2 | // |
| 3 | // This source code is licensed under the MIT license found in the |
| 4 | // LICENSE file in the root directory of this source tree. |
| 5 | |
| 6 | //! Simple timing accumulator for profiling compiler passes. |
| 7 | //! |
| 8 | //! Uses `std::time::Instant` unconditionally (cheap when not storing results). |
| 9 | //! Controlled by the `__profiling` flag in plugin options. |
| 10 | |
| 11 | use serde::Serialize; |
| 12 | use std::time::{Duration, Instant}; |
| 13 | |
| 14 | /// A single timing entry recording how long a named phase took. |
| 15 | #[derive(Debug, Clone, Serialize)] |
| 16 | pub struct TimingEntry { |
| 17 | pub name: String, |
| 18 | pub duration_us: u64, |
| 19 | } |
| 20 | |
| 21 | /// Accumulates timing data for compiler passes. |
| 22 | pub struct TimingData { |
| 23 | enabled: bool, |
| 24 | entries: Vec<(String, Duration)>, |
| 25 | current_name: Option<String>, |
| 26 | current_start: Option<Instant>, |
| 27 | } |
| 28 | |
| 29 | impl TimingData { |
| 30 | /// Create a new TimingData. If `enabled` is false, all operations are no-ops. |
| 31 | pub fn new(enabled: bool) -> Self { |
| 32 | Self { |
| 33 | enabled, |
| 34 | entries: Vec::new(), |
| 35 | current_name: None, |
| 36 | current_start: None, |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | /// Start timing a named phase. Stops any currently running phase first. |
| 41 | pub fn start(&mut self, name: &str) { |
| 42 | if !self.enabled { |
| 43 | return; |
| 44 | } |
| 45 | // Stop any currently running phase |
| 46 | if self.current_start.is_some() { |
| 47 | self.stop(); |
| 48 | } |
| 49 | self.current_name = Some(name.to_string()); |
| 50 | self.current_start = Some(Instant::now()); |
| 51 | } |
| 52 | |
| 53 | /// Stop the currently running phase and record its duration. |
| 54 | pub fn stop(&mut self) { |
| 55 | if !self.enabled { |
| 56 | return; |
| 57 | } |
| 58 | if let (Some(name), Some(start)) = (self.current_name.take(), self.current_start.take()) { |
| 59 | self.entries.push((name, start.elapsed())); |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | /// Consume this TimingData and return the collected entries. |
| 64 | pub fn into_entries(mut self) -> Vec<TimingEntry> { |
| 65 | // Stop any still-running phase |
| 66 | self.stop(); |
| 67 | self.entries |
| 68 | .into_iter() |
| 69 | .map(|(name, duration)| TimingEntry { |
| 70 | name, |
| 71 | duration_us: duration.as_micros() as u64, |
| 72 | }) |
| 73 | .collect() |
| 74 | } |
| 75 | } |