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}