quantrs2_anneal/scientific_performance_optimization/
profiling.rs1use std::collections::{HashMap, VecDeque};
7use std::time::{Duration, Instant};
8
9use super::config::{GPUAccelerationConfig, ProfilingConfig};
10
11pub struct PerformanceProfiler {
13 pub config: ProfilingConfig,
15 pub cpu_profiler: CPUProfiler,
17 pub memory_profiler: MemoryProfiler,
19 pub io_profiler: IOProfiler,
21 pub metrics: PerformanceMetrics,
23}
24
25impl PerformanceProfiler {
26 #[must_use]
28 pub fn new(config: ProfilingConfig) -> Self {
29 Self {
30 config,
31 cpu_profiler: CPUProfiler::new(),
32 memory_profiler: MemoryProfiler::new(),
33 io_profiler: IOProfiler::new(),
34 metrics: PerformanceMetrics::default(),
35 }
36 }
37
38 pub fn start(&mut self) {
40 self.cpu_profiler.start();
41 self.memory_profiler.start();
42 self.io_profiler.start();
43 }
44
45 pub fn stop(&mut self) -> PerformanceMetrics {
47 self.cpu_profiler.stop();
48 self.memory_profiler.stop();
49 self.io_profiler.stop();
50 self.metrics.clone()
51 }
52
53 pub fn snapshot(&mut self) {
55 self.cpu_profiler.sample();
56 self.memory_profiler.sample();
57 self.io_profiler.sample();
58 }
59}
60
61#[derive(Debug)]
63pub struct CPUProfiler {
64 pub cpu_samples: VecDeque<CPUSample>,
66 pub function_stats: HashMap<String, FunctionStatistics>,
68 pub config: CPUProfilingConfig,
70 pub is_active: bool,
72}
73
74impl CPUProfiler {
75 #[must_use]
77 pub fn new() -> Self {
78 Self {
79 cpu_samples: VecDeque::new(),
80 function_stats: HashMap::new(),
81 config: CPUProfilingConfig::default(),
82 is_active: false,
83 }
84 }
85
86 pub fn start(&mut self) {
88 self.is_active = true;
89 self.cpu_samples.clear();
90 }
91
92 pub fn stop(&mut self) {
94 self.is_active = false;
95 }
96
97 pub fn sample(&mut self) {
99 if !self.is_active {
100 return;
101 }
102
103 let sample = CPUSample {
104 timestamp: Instant::now(),
105 usage_percent: 0.0, active_threads: std::thread::available_parallelism()
107 .map(|p| p.get())
108 .unwrap_or(1),
109 context_switches: 0,
110 };
111
112 self.cpu_samples.push_back(sample);
113
114 while self.cpu_samples.len() > self.config.max_samples {
116 self.cpu_samples.pop_front();
117 }
118 }
119
120 pub fn record_function_call(&mut self, function_name: &str, duration: Duration) {
122 let stats = self
123 .function_stats
124 .entry(function_name.to_string())
125 .or_insert_with(|| FunctionStatistics::new(function_name));
126
127 stats.call_count += 1;
128 stats.total_time += duration;
129 stats.average_time = stats.total_time / stats.call_count as u32;
130
131 if duration > stats.max_time {
132 stats.max_time = duration;
133 }
134 if stats.min_time == Duration::ZERO || duration < stats.min_time {
135 stats.min_time = duration;
136 }
137 }
138
139 #[must_use]
141 pub fn average_usage(&self) -> f64 {
142 if self.cpu_samples.is_empty() {
143 return 0.0;
144 }
145 let sum: f64 = self.cpu_samples.iter().map(|s| s.usage_percent).sum();
146 sum / self.cpu_samples.len() as f64
147 }
148}
149
150impl Default for CPUProfiler {
151 fn default() -> Self {
152 Self::new()
153 }
154}
155
156#[derive(Debug, Clone)]
158pub struct CPUSample {
159 pub timestamp: Instant,
161 pub usage_percent: f64,
163 pub active_threads: usize,
165 pub context_switches: u64,
167}
168
169#[derive(Debug, Clone)]
171pub struct FunctionStatistics {
172 pub function_name: String,
174 pub call_count: u64,
176 pub total_time: Duration,
178 pub average_time: Duration,
180 pub max_time: Duration,
182 pub min_time: Duration,
184}
185
186impl FunctionStatistics {
187 #[must_use]
189 pub fn new(function_name: &str) -> Self {
190 Self {
191 function_name: function_name.to_string(),
192 call_count: 0,
193 total_time: Duration::ZERO,
194 average_time: Duration::ZERO,
195 max_time: Duration::ZERO,
196 min_time: Duration::ZERO,
197 }
198 }
199}
200
201#[derive(Debug, Clone)]
203pub struct CPUProfilingConfig {
204 pub max_samples: usize,
206 pub sampling_interval: Duration,
208 pub enable_function_profiling: bool,
210}
211
212impl Default for CPUProfilingConfig {
213 fn default() -> Self {
214 Self {
215 max_samples: 1000,
216 sampling_interval: Duration::from_millis(100),
217 enable_function_profiling: true,
218 }
219 }
220}
221
222#[derive(Debug, Clone, Default)]
224pub struct MemoryProfiler {
225 pub samples: VecDeque<MemorySample>,
227 pub is_active: bool,
229}
230
231impl MemoryProfiler {
232 #[must_use]
234 pub const fn new() -> Self {
235 Self {
236 samples: VecDeque::new(),
237 is_active: false,
238 }
239 }
240
241 pub fn start(&mut self) {
243 self.is_active = true;
244 self.samples.clear();
245 }
246
247 pub fn stop(&mut self) {
249 self.is_active = false;
250 }
251
252 pub fn sample(&mut self) {
254 if !self.is_active {
255 return;
256 }
257
258 let sample = MemorySample {
259 timestamp: Instant::now(),
260 heap_usage: 0,
261 stack_usage: 0,
262 total_allocated: 0,
263 };
264
265 self.samples.push_back(sample);
266 }
267}
268
269#[derive(Debug, Clone)]
271pub struct MemorySample {
272 pub timestamp: Instant,
274 pub heap_usage: usize,
276 pub stack_usage: usize,
278 pub total_allocated: usize,
280}
281
282#[derive(Debug, Clone, Default)]
284pub struct IOProfiler {
285 pub samples: VecDeque<IOSample>,
287 pub is_active: bool,
289}
290
291impl IOProfiler {
292 #[must_use]
294 pub const fn new() -> Self {
295 Self {
296 samples: VecDeque::new(),
297 is_active: false,
298 }
299 }
300
301 pub fn start(&mut self) {
303 self.is_active = true;
304 self.samples.clear();
305 }
306
307 pub fn stop(&mut self) {
309 self.is_active = false;
310 }
311
312 pub fn sample(&mut self) {
314 if !self.is_active {
315 return;
316 }
317
318 let sample = IOSample {
319 timestamp: Instant::now(),
320 bytes_read: 0,
321 bytes_written: 0,
322 io_operations: 0,
323 };
324
325 self.samples.push_back(sample);
326 }
327}
328
329#[derive(Debug, Clone)]
331pub struct IOSample {
332 pub timestamp: Instant,
334 pub bytes_read: u64,
336 pub bytes_written: u64,
338 pub io_operations: u64,
340}
341
342#[derive(Debug, Clone, Default)]
344pub struct PerformanceMetrics {
345 pub performance_score: f64,
347 pub cpu_utilization: f64,
349 pub memory_utilization: f64,
351 pub io_throughput: f64,
353}
354
355pub struct GPUAccelerator {
357 pub config: GPUAccelerationConfig,
359 pub devices: Vec<GPUDevice>,
361 pub memory_manager: GPUMemoryManager,
363 pub kernel_registry: KernelRegistry,
365}
366
367impl GPUAccelerator {
368 #[must_use]
370 pub fn new(config: GPUAccelerationConfig) -> Self {
371 Self {
372 config,
373 devices: Vec::new(),
374 memory_manager: GPUMemoryManager::new(),
375 kernel_registry: KernelRegistry::new(),
376 }
377 }
378
379 #[must_use]
381 pub fn is_available(&self) -> bool {
382 self.config.enable_gpu && !self.devices.is_empty()
383 }
384
385 #[must_use]
387 pub fn device_count(&self) -> usize {
388 self.devices.len()
389 }
390
391 #[must_use]
393 pub fn get_device(&self, device_id: usize) -> Option<&GPUDevice> {
394 self.devices.iter().find(|d| d.device_id == device_id)
395 }
396
397 pub fn detect_devices(&mut self) {
399 self.devices.clear();
402 }
403}
404
405#[derive(Debug)]
407pub struct GPUDevice {
408 pub device_id: usize,
410 pub device_name: String,
412 pub compute_capability: (u32, u32),
414 pub total_memory: usize,
416 pub available_memory: usize,
418 pub status: GPUDeviceStatus,
420}
421
422impl GPUDevice {
423 #[must_use]
425 pub fn new(device_id: usize, device_name: String) -> Self {
426 Self {
427 device_id,
428 device_name,
429 compute_capability: (0, 0),
430 total_memory: 0,
431 available_memory: 0,
432 status: GPUDeviceStatus::Available,
433 }
434 }
435
436 #[must_use]
438 pub fn is_available(&self) -> bool {
439 self.status == GPUDeviceStatus::Available
440 }
441
442 #[must_use]
444 pub fn memory_utilization(&self) -> f64 {
445 if self.total_memory == 0 {
446 return 0.0;
447 }
448 (self.total_memory - self.available_memory) as f64 / self.total_memory as f64
449 }
450}
451
452#[derive(Debug, Clone, PartialEq, Eq)]
454pub enum GPUDeviceStatus {
455 Available,
457 Busy,
459 Error,
461 Unsupported,
463}
464
465#[derive(Debug, Clone, Default)]
467pub struct GPUMemoryManager {
468 pub allocated_buffers: HashMap<String, GPUBuffer>,
470 pub total_allocated: usize,
472}
473
474impl GPUMemoryManager {
475 #[must_use]
477 pub fn new() -> Self {
478 Self {
479 allocated_buffers: HashMap::new(),
480 total_allocated: 0,
481 }
482 }
483
484 pub fn allocate(&mut self, name: &str, size: usize) -> Result<(), String> {
486 let buffer = GPUBuffer {
487 name: name.to_string(),
488 size,
489 device_ptr: 0, };
491
492 self.allocated_buffers.insert(name.to_string(), buffer);
493 self.total_allocated += size;
494 Ok(())
495 }
496
497 pub fn free(&mut self, name: &str) -> Result<(), String> {
499 if let Some(buffer) = self.allocated_buffers.remove(name) {
500 self.total_allocated = self.total_allocated.saturating_sub(buffer.size);
501 Ok(())
502 } else {
503 Err(format!("Buffer {name} not found"))
504 }
505 }
506}
507
508#[derive(Debug, Clone)]
510pub struct GPUBuffer {
511 pub name: String,
513 pub size: usize,
515 pub device_ptr: usize,
517}
518
519#[derive(Debug, Clone, Default)]
521pub struct KernelRegistry {
522 pub kernels: HashMap<String, GPUKernel>,
524}
525
526impl KernelRegistry {
527 #[must_use]
529 pub fn new() -> Self {
530 Self {
531 kernels: HashMap::new(),
532 }
533 }
534
535 pub fn register(&mut self, name: &str, kernel: GPUKernel) {
537 self.kernels.insert(name.to_string(), kernel);
538 }
539
540 #[must_use]
542 pub fn get(&self, name: &str) -> Option<&GPUKernel> {
543 self.kernels.get(name)
544 }
545}
546
547#[derive(Debug, Clone)]
549pub struct GPUKernel {
550 pub name: String,
552 pub block_size: usize,
554 pub grid_size: usize,
556 pub shared_memory: usize,
558}
559
560impl GPUKernel {
561 #[must_use]
563 pub fn new(name: &str) -> Self {
564 Self {
565 name: name.to_string(),
566 block_size: 256,
567 grid_size: 1024,
568 shared_memory: 0,
569 }
570 }
571}