quantrs2_core/optimizations/
profiling_integration.rs1use crate::error::QuantRS2Result;
7use scirs2_core::profiling::{MemoryTracker, Profiler, Timer};
8use std::collections::HashMap;
9use std::sync::{Arc, Mutex, OnceLock};
10use std::time::{Duration, Instant};
11
12use std::fmt::Write;
13#[derive(Debug, Clone, serde::Serialize)]
15pub struct QuantumOperationProfile {
16 pub operation_name: String,
17 pub execution_count: u64,
18 pub total_time: Duration,
19 pub average_time: Duration,
20 pub min_time: Duration,
21 pub max_time: Duration,
22 pub memory_usage: u64,
23 pub gate_count: u64,
24}
25
26pub struct QuantumProfiler {
28 profiles: Arc<Mutex<HashMap<String, QuantumOperationProfile>>>,
30 active_timers: Arc<Mutex<HashMap<String, Instant>>>,
32 enabled: Arc<Mutex<bool>>,
34 memory_tracker: Arc<Mutex<Option<MemoryTracker>>>,
36}
37
38impl Default for QuantumProfiler {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl QuantumProfiler {
45 pub fn new() -> Self {
47 Self {
48 profiles: Arc::new(Mutex::new(HashMap::new())),
49 active_timers: Arc::new(Mutex::new(HashMap::new())),
50 enabled: Arc::new(Mutex::new(false)),
51 memory_tracker: Arc::new(Mutex::new(None)),
52 }
53 }
54
55 pub fn enable(&self) {
57 *self.enabled.lock().expect("Profiler enabled lock poisoned") = true;
58
59 if let Ok(mut profiler) = Profiler::global().lock() {
61 profiler.start();
62 }
63 }
64
65 pub fn disable(&self) {
67 *self.enabled.lock().expect("Profiler enabled lock poisoned") = false;
68
69 if let Ok(mut profiler) = Profiler::global().lock() {
71 profiler.stop();
72 }
73 }
74
75 pub fn is_enabled(&self) -> bool {
77 *self.enabled.lock().expect("Profiler enabled lock poisoned")
78 }
79
80 pub fn start_operation(&self, operation_name: &str) {
82 if !self.is_enabled() {
83 return;
84 }
85
86 let mut timers = self
87 .active_timers
88 .lock()
89 .expect("Active timers lock poisoned");
90 timers.insert(operation_name.to_string(), Instant::now());
91
92 let mut tracker = self
94 .memory_tracker
95 .lock()
96 .expect("Memory tracker lock poisoned");
97 *tracker = Some(MemoryTracker::start(operation_name));
98 }
99
100 pub fn end_operation(&self, operation_name: &str, gate_count: u64) {
102 if !self.is_enabled() {
103 return;
104 }
105
106 let start_time = {
107 let mut timers = self
108 .active_timers
109 .lock()
110 .expect("Active timers lock poisoned");
111 timers.remove(operation_name)
112 };
113
114 if let Some(start) = start_time {
115 let execution_time = start.elapsed();
116
117 let memory_usage = {
119 let mut tracker = self
120 .memory_tracker
121 .lock()
122 .expect("Memory tracker lock poisoned");
123 if let Some(mem_tracker) = tracker.take() {
124 mem_tracker.stop();
125 0 } else {
128 0
129 }
130 };
131
132 let mut profiles = self.profiles.lock().expect("Profiles lock poisoned");
134 let profile = profiles
135 .entry(operation_name.to_string())
136 .or_insert_with(|| QuantumOperationProfile {
137 operation_name: operation_name.to_string(),
138 execution_count: 0,
139 total_time: Duration::ZERO,
140 average_time: Duration::ZERO,
141 min_time: Duration::MAX,
142 max_time: Duration::ZERO,
143 memory_usage: 0,
144 gate_count: 0,
145 });
146
147 profile.execution_count += 1;
148 profile.total_time += execution_time;
149 profile.average_time = profile.total_time / profile.execution_count as u32;
150 profile.min_time = profile.min_time.min(execution_time);
151 profile.max_time = profile.max_time.max(execution_time);
152 profile.memory_usage += memory_usage;
153 profile.gate_count += gate_count;
154 }
155 }
156
157 pub fn profile_operation<F, R>(&self, operation_name: &str, gate_count: u64, operation: F) -> R
159 where
160 F: FnOnce() -> R,
161 {
162 if !self.is_enabled() {
163 return operation();
164 }
165
166 self.start_operation(operation_name);
167 let result = operation();
168 self.end_operation(operation_name, gate_count);
169 result
170 }
171
172 pub fn get_profiles(&self) -> HashMap<String, QuantumOperationProfile> {
174 self.profiles
175 .lock()
176 .expect("Profiles lock poisoned")
177 .clone()
178 }
179
180 pub fn get_operation_profile(&self, operation_name: &str) -> Option<QuantumOperationProfile> {
182 self.profiles
183 .lock()
184 .expect("Profiles lock poisoned")
185 .get(operation_name)
186 .cloned()
187 }
188
189 pub fn generate_report(&self) -> String {
191 let profiles = self.get_profiles();
192 let mut report = String::new();
193
194 report.push_str("=== QuantRS2 Performance Profiling Report ===\n\n");
195
196 if profiles.is_empty() {
197 report.push_str("No profiling data available.\n");
198 return report;
199 }
200
201 let mut sorted_profiles: Vec<_> = profiles.values().collect();
203 sorted_profiles.sort_by(|a, b| b.total_time.cmp(&a.total_time));
204
205 writeln!(
206 report,
207 "{:<30} {:<10} {:<12} {:<12} {:<12} {:<12} {:<10}",
208 "Operation", "Count", "Total (ms)", "Avg (ms)", "Min (ms)", "Max (ms)", "Gates"
209 )
210 .expect("Writing to String cannot fail");
211 report.push_str(&"-".repeat(110));
212 report.push('\n');
213
214 for profile in &sorted_profiles {
215 writeln!(
216 report,
217 "{:<30} {:<10} {:<12.3} {:<12.3} {:<12.3} {:<12.3} {:<10}",
218 profile.operation_name,
219 profile.execution_count,
220 profile.total_time.as_secs_f64() * 1000.0,
221 profile.average_time.as_secs_f64() * 1000.0,
222 profile.min_time.as_secs_f64() * 1000.0,
223 profile.max_time.as_secs_f64() * 1000.0,
224 profile.gate_count,
225 )
226 .expect("Writing to String cannot fail");
227 }
228
229 report.push_str("\n=== Performance Insights ===\n");
230
231 if let Some(slowest) = sorted_profiles.first() {
233 writeln!(
234 report,
235 "Most time-consuming operation: {} ({:.3}ms total)",
236 slowest.operation_name,
237 slowest.total_time.as_secs_f64() * 1000.0
238 )
239 .expect("Writing to String cannot fail");
240 }
241
242 let most_frequent = sorted_profiles.iter().max_by_key(|p| p.execution_count);
244 if let Some(frequent) = most_frequent {
245 writeln!(
246 report,
247 "Most frequent operation: {} ({} executions)",
248 frequent.operation_name, frequent.execution_count
249 )
250 .expect("Writing to String cannot fail");
251 }
252
253 let total_gates: u64 = profiles.values().map(|p| p.gate_count).sum();
255 let total_time: Duration = profiles.values().map(|p| p.total_time).sum();
256 if total_time.as_secs_f64() > 0.0 {
257 let gate_throughput = total_gates as f64 / total_time.as_secs_f64();
258 writeln!(
259 report,
260 "Total gate throughput: {gate_throughput:.0} gates/second"
261 )
262 .expect("Writing to String cannot fail");
263 }
264
265 report
266 }
267
268 pub fn clear(&self) {
270 self.profiles
271 .lock()
272 .expect("Profiles lock poisoned")
273 .clear();
274 self.active_timers
275 .lock()
276 .expect("Active timers lock poisoned")
277 .clear();
278 }
279
280 pub fn export_json(&self) -> QuantRS2Result<String> {
282 let profiles = self.get_profiles();
283 serde_json::to_string_pretty(&profiles).map_err(|e| e.into()) }
285}
286
287static GLOBAL_QUANTUM_PROFILER: OnceLock<QuantumProfiler> = OnceLock::new();
289
290pub fn global_quantum_profiler() -> &'static QuantumProfiler {
292 GLOBAL_QUANTUM_PROFILER.get_or_init(QuantumProfiler::new)
293}
294
295pub fn enable_quantum_profiling() {
297 global_quantum_profiler().enable();
298}
299
300pub fn disable_quantum_profiling() {
302 global_quantum_profiler().disable();
303}
304
305pub fn is_profiling_active() -> bool {
307 global_quantum_profiler().is_enabled()
308}
309
310#[macro_export]
312macro_rules! profile_quantum_operation {
313 ($operation_name:expr, $gate_count:expr, $operation:expr) => {{
314 $crate::optimizations::profiling_integration::global_quantum_profiler().profile_operation(
315 $operation_name,
316 $gate_count,
317 || $operation,
318 )
319 }};
320}
321
322#[macro_export]
324macro_rules! profile_gate_operation {
325 ($gate_name:expr, $operation:expr) => {{
326 $crate::optimizations::profiling_integration::global_quantum_profiler().profile_operation(
327 $gate_name,
328 1,
329 || $operation,
330 )
331 }};
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use std::thread;
338
339 #[test]
340 fn test_basic_profiling() {
341 let profiler = QuantumProfiler::new();
342 profiler.enable();
343
344 let result = profiler.profile_operation("test_gate", 1, || {
346 thread::sleep(Duration::from_millis(10));
347 42
348 });
349
350 assert_eq!(result, 42);
351
352 let profiles = profiler.get_profiles();
353 assert!(profiles.contains_key("test_gate"));
354
355 let test_profile = &profiles["test_gate"];
356 assert_eq!(test_profile.execution_count, 1);
357 assert_eq!(test_profile.gate_count, 1);
358 assert!(test_profile.total_time >= Duration::from_millis(10));
359 }
360
361 #[test]
362 fn test_multiple_operations() {
363 let profiler = QuantumProfiler::new();
364 profiler.enable();
365
366 for i in 0..5 {
368 profiler.profile_operation("hadamard", 1, || {
369 thread::sleep(Duration::from_millis(1));
370 });
371 }
372
373 for i in 0..3 {
374 profiler.profile_operation("cnot", 2, || {
375 thread::sleep(Duration::from_millis(2));
376 });
377 }
378
379 let profiles = profiler.get_profiles();
380
381 let hadamard_profile = &profiles["hadamard"];
382 assert_eq!(hadamard_profile.execution_count, 5);
383 assert_eq!(hadamard_profile.gate_count, 5);
384
385 let cnot_profile = &profiles["cnot"];
386 assert_eq!(cnot_profile.execution_count, 3);
387 assert_eq!(cnot_profile.gate_count, 6); }
389
390 #[test]
391 fn test_profiling_disabled() {
392 let profiler = QuantumProfiler::new();
393 let result = profiler.profile_operation("test_gate", 1, || 42);
396
397 assert_eq!(result, 42);
398
399 let profiles = profiler.get_profiles();
400 assert!(profiles.is_empty()); }
402
403 #[test]
404 fn test_report_generation() {
405 let profiler = QuantumProfiler::new();
406 profiler.enable();
407
408 profiler.profile_operation("fast_gate", 1, || {
409 thread::sleep(Duration::from_millis(1));
410 });
411
412 profiler.profile_operation("slow_gate", 1, || {
413 thread::sleep(Duration::from_millis(10));
414 });
415
416 let report = profiler.generate_report();
417 assert!(report.contains("QuantRS2 Performance Profiling Report"));
418 assert!(report.contains("fast_gate"));
419 assert!(report.contains("slow_gate"));
420 assert!(report.contains("Performance Insights"));
421 }
422
423 #[test]
424 fn test_json_export() {
425 let profiler = QuantumProfiler::new();
426 profiler.enable();
427
428 profiler.profile_operation("test_operation", 1, || {
429 thread::sleep(Duration::from_millis(1));
430 });
431
432 let json_result = profiler.export_json();
433 assert!(json_result.is_ok());
434
435 let json = json_result.expect("Failed to export JSON");
436 assert!(json.contains("test_operation"));
437 assert!(json.contains("execution_count"));
438 }
439}