use crate::{ProfileEvent, TorshResult};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
use torsh_core::TorshError;
pub struct CpuProfiler {
events: Arc<Mutex<Vec<ProfileEvent>>>,
start_time: Instant,
enabled: bool,
}
impl Default for CpuProfiler {
fn default() -> Self {
Self::new()
}
}
impl CpuProfiler {
pub fn new() -> Self {
Self {
events: Arc::new(Mutex::new(Vec::new())),
start_time: Instant::now(),
enabled: false,
}
}
pub fn enable(&mut self) {
self.enabled = true;
self.start_time = Instant::now();
if let Ok(mut events) = self.events.lock() {
events.clear();
}
}
pub fn disable(&mut self) {
self.enabled = false;
}
pub fn record_event(&self, name: &str, category: &str, duration: Duration) -> TorshResult<()> {
if !self.enabled {
return Ok(());
}
let mut events = self.events.lock().map_err(|_| {
TorshError::InvalidArgument("Failed to acquire lock on events".to_string())
})?;
let thread_id = thread::current().id();
let thread_id_num = format!("{thread_id:?}").parse::<usize>().unwrap_or(0);
let start_us = self.start_time.elapsed().as_micros() as u64;
let duration_us = duration.as_micros() as u64;
events.push(ProfileEvent {
name: name.to_string(),
category: category.to_string(),
start_us,
duration_us,
thread_id: thread_id_num,
operation_count: None,
flops: None,
bytes_transferred: None,
stack_trace: None,
});
Ok(())
}
pub fn get_events(&self) -> TorshResult<Vec<ProfileEvent>> {
let events = self.events.lock().map_err(|_| {
TorshError::InvalidArgument("Failed to acquire lock on events".to_string())
})?;
Ok(events.clone())
}
pub fn clear(&self) -> TorshResult<()> {
let mut events = self.events.lock().map_err(|_| {
TorshError::InvalidArgument("Failed to acquire lock on events".to_string())
})?;
events.clear();
Ok(())
}
}
pub struct ProfileScope {
profiler: Arc<CpuProfiler>,
name: String,
category: String,
start: Instant,
}
impl ProfileScope {
pub fn new(profiler: Arc<CpuProfiler>, name: &str, category: &str) -> Self {
Self {
profiler,
name: name.to_string(),
category: category.to_string(),
start: Instant::now(),
}
}
pub fn simple(name: String, category: String) -> Self {
let mut cpu_profiler = CpuProfiler::new();
cpu_profiler.enable();
let profiler = Arc::new(cpu_profiler);
Self {
profiler,
name,
category,
start: Instant::now(),
}
}
}
impl Drop for ProfileScope {
fn drop(&mut self) {
let duration = self.start.elapsed();
let _ = self
.profiler
.record_event(&self.name, &self.category, duration);
}
}
pub fn get_cpu_usage() -> TorshResult<f64> {
Ok(0.0) }
pub fn get_cpu_frequency() -> TorshResult<u64> {
Ok(2400000000) }
pub fn profile_cpu() -> TorshResult<Vec<ProfileEvent>> {
let mut profiler = CpuProfiler::new();
profiler.enable();
let start = Instant::now();
let _: Vec<i32> = (0..1000).map(|x| x * x).collect();
let duration = start.elapsed();
profiler.record_event("matrix_mul", "compute", duration)?;
profiler.get_events()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpu_profiler_creation() {
let profiler = CpuProfiler::new();
assert!(!profiler.enabled);
}
#[test]
fn test_cpu_profiler_enable_disable() {
let mut profiler = CpuProfiler::new();
profiler.enable();
assert!(profiler.enabled);
profiler.disable();
assert!(!profiler.enabled);
}
#[test]
fn test_cpu_profiler_record_event() {
let mut profiler = CpuProfiler::new();
profiler.enable();
let duration = Duration::from_millis(10);
profiler
.record_event("test_event", "test_category", duration)
.unwrap();
let events = profiler.get_events().unwrap();
assert_eq!(events.len(), 1);
assert_eq!(events[0].name, "test_event");
assert_eq!(events[0].category, "test_category");
}
#[test]
fn test_profile_scope() {
let mut profiler = CpuProfiler::new();
profiler.enable();
let profiler_arc = Arc::new(profiler);
{
let _scope = ProfileScope::new(profiler_arc.clone(), "test_scope", "test");
std::thread::sleep(Duration::from_millis(1));
}
let events = profiler_arc.get_events().unwrap();
assert_eq!(events.len(), 1);
assert_eq!(events[0].name, "test_scope");
}
}