kryst/utils/
profiling.rs

1//! Lightweight profiling utilities for Krylov solvers.
2//!
3//! This module provides RAII-based stage guards for timing major solver phases,
4//! similar to PETSc's profiling stages. Profiling can be enabled/disabled at runtime
5//! via global configuration.
6
7use std::sync::atomic::{AtomicBool, Ordering};
8use std::time::Instant;
9
10/// Global flag to enable/disable profiling at runtime.
11static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
12
13/// Global flag to enable/disable monitoring at runtime.
14static MONITORING_ENABLED: AtomicBool = AtomicBool::new(false);
15
16/// Enable profiling globally.
17pub fn enable_profiling() {
18    PROFILING_ENABLED.store(true, Ordering::Relaxed);
19}
20
21/// Disable profiling globally.
22pub fn disable_profiling() {
23    PROFILING_ENABLED.store(false, Ordering::Relaxed);
24}
25
26/// Check if profiling is currently enabled.
27pub fn is_profiling_enabled() -> bool {
28    PROFILING_ENABLED.load(Ordering::Relaxed)
29}
30
31/// Enable monitoring globally.
32pub fn enable_monitoring() {
33    MONITORING_ENABLED.store(true, Ordering::Relaxed);
34}
35
36/// Disable monitoring globally.
37pub fn disable_monitoring() {
38    MONITORING_ENABLED.store(false, Ordering::Relaxed);
39}
40
41/// Check if monitoring is currently enabled.
42pub fn is_monitoring_enabled() -> bool {
43    MONITORING_ENABLED.load(Ordering::Relaxed)
44}
45
46/// RAII guard for profiling solver stages.
47///
48/// This struct automatically logs stage entry on creation and stage exit on drop
49/// when profiling is enabled. When profiling is disabled, this becomes a very
50/// low-cost abstraction.
51///
52/// # Example
53/// ```rust
54/// use kryst::utils::profiling::{StageGuard, enable_profiling};
55///
56/// enable_profiling();
57/// {
58///     let _setup = StageGuard::new("KSPSetup");
59///     // ... setup code ...
60/// } // "leave stage: KSPSetup" logged here if profiling enabled
61/// ```
62pub struct StageGuard {
63    name: &'static str,
64    start_time: Option<Instant>,
65}
66
67impl StageGuard {
68    /// Create a new stage guard and log entry if profiling is enabled.
69    ///
70    /// # Arguments
71    /// * `name` - Static string name of the stage
72    ///
73    /// # Returns
74    /// * A new `StageGuard` that will log stage exit on drop if profiling is enabled
75    pub fn new(name: &'static str) -> Self {
76        let start_time = if is_profiling_enabled() {
77            #[cfg(feature = "logging")]
78            log::trace!("---- enter stage: {name}");
79            #[cfg(not(feature = "logging"))]
80            eprintln!("---- enter stage: {}", name);
81            Some(Instant::now())
82        } else {
83            None
84        };
85
86        StageGuard { name, start_time }
87    }
88
89    /// Get the elapsed time since the stage started.
90    /// Returns None if profiling was disabled when the stage started.
91    pub fn elapsed(&self) -> Option<std::time::Duration> {
92        self.start_time.map(|start| start.elapsed())
93    }
94}
95
96impl Drop for StageGuard {
97    fn drop(&mut self) {
98        if let Some(start_time) = self.start_time {
99            let elapsed = start_time.elapsed();
100            #[cfg(feature = "logging")]
101            log::trace!("---- leave stage: {} (took {:?})", self.name, elapsed);
102            #[cfg(not(feature = "logging"))]
103            eprintln!("---- leave stage: {} (took {:?})", self.name, elapsed);
104        }
105    }
106}
107
108/// Macro for timing a code block with automatic stage guard creation.
109///
110/// This is a convenience macro that creates a stage guard for a named block.
111/// The guard is automatically dropped at the end of the block.
112///
113/// # Example
114/// ```rust,ignore
115/// use kryst::utils::profiling::time_stage;
116///
117/// time_stage!("MatVec", {
118///     // ... matrix-vector multiplication code ...
119/// });
120/// ```
121#[macro_export]
122macro_rules! time_stage {
123    ($name:expr, $block:block) => {{
124        let _guard = $crate::utils::profiling::StageGuard::new($name);
125        $block
126    }};
127}
128
129/// Conditionally execute profiling code only when profiling is enabled.
130///
131/// This macro helps avoid the overhead of profiling operations when profiling
132/// is disabled at runtime.
133///
134/// # Example
135/// ```rust,ignore
136/// use kryst::utils::profiling::with_profiling;
137///
138/// with_profiling!(|| {
139///     eprintln!("Starting expensive operation");
140///     let start = std::time::Instant::now();
141///     // ... operation ...
142///     eprintln!("Operation took {:?}", start.elapsed());
143/// });
144/// ```
145#[macro_export]
146macro_rules! with_profiling {
147    ($closure:expr) => {
148        if $crate::utils::profiling::is_profiling_enabled() {
149            $closure();
150        }
151    };
152}
153
154/// Conditionally execute monitoring code only when monitoring is enabled.
155///
156/// This macro helps avoid the overhead of monitoring operations when monitoring
157/// is disabled at runtime.
158///
159/// # Example
160/// ```rust,ignore
161/// use kryst::utils::profiling::with_monitoring;
162///
163/// with_monitoring!(|| {
164///     eprintln!("Iteration {}: residual = {:.2e}", iter, residual);
165/// });
166/// ```
167#[macro_export]
168macro_rules! with_monitoring {
169    ($closure:expr) => {
170        if $crate::utils::profiling::is_monitoring_enabled() {
171            $closure();
172        }
173    };
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn test_stage_guard_creation() {
182        // This should not panic regardless of logging feature
183        let _guard = StageGuard::new("TestStage");
184    }
185
186    #[test]
187    fn test_nested_stages() {
188        let _outer = StageGuard::new("OuterStage");
189        {
190            let _inner = StageGuard::new("InnerStage");
191            // Inner should drop first
192        }
193        // Outer drops last
194    }
195}