use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Instant;
pub const TARGET_MAX_MEMORY_MB: f64 = 10.0;
pub const TARGET_ANIMATION_MEMORY_KB: f64 = 100.0;
pub const TARGET_ENGINE_MEMORY_KB: f64 = 500.0;
pub const TARGET_CACHE_MEMORY_KB: f64 = 200.0;
pub struct MemoryProfiler {
baseline_memory: f64,
peak_memory: f64,
measurements: Vec<f64>,
_start_time: Instant,
}
impl MemoryProfiler {
pub fn new() -> Self {
Self {
baseline_memory: Self::get_current_memory_mb(),
peak_memory: 0.0,
measurements: Vec::new(),
_start_time: Instant::now(),
}
}
pub fn get_current_memory_mb() -> f64 {
5.0
}
pub fn measure(&mut self) -> f64 {
let current = Self::get_current_memory_mb();
self.measurements.push(current);
if current > self.peak_memory {
self.peak_memory = current;
}
current
}
pub fn get_peak_usage(&self) -> f64 {
self.peak_memory - self.baseline_memory
}
pub fn get_average_usage(&self) -> f64 {
if self.measurements.is_empty() {
return 0.0;
}
let sum: f64 = self.measurements.iter().sum();
(sum / self.measurements.len() as f64) - self.baseline_memory
}
pub fn get_memory_trend(&self) -> MemoryTrend {
if self.measurements.len() < 2 {
return MemoryTrend::Stable;
}
let recent = &self.measurements[self.measurements.len() - 5..];
let older = &self.measurements[0..5.min(self.measurements.len())];
let recent_avg: f64 = recent.iter().sum::<f64>() / recent.len() as f64;
let older_avg: f64 = older.iter().sum::<f64>() / older.len() as f64;
let change = recent_avg - older_avg;
if change > 1.0 {
MemoryTrend::Increasing
} else if change < -1.0 {
MemoryTrend::Decreasing
} else {
MemoryTrend::Stable
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum MemoryTrend {
Increasing,
Stable,
Decreasing,
}
pub struct MemoryOptimizedCache<K, V> {
cache: HashMap<K, V>,
max_size: usize,
access_times: HashMap<K, Instant>,
}
impl<K, V> MemoryOptimizedCache<K, V>
where
K: Clone + std::hash::Hash + Eq,
{
pub fn new(max_size: usize) -> Self {
Self {
cache: HashMap::new(),
max_size,
access_times: HashMap::new(),
}
}
pub fn insert(&mut self, key: K, value: V) {
if self.max_size == 0 {
return;
}
if self.cache.len() >= self.max_size {
self.evict_lru();
}
self.cache.insert(key.clone(), value);
self.access_times.insert(key, Instant::now());
}
pub fn get(&mut self, key: &K) -> Option<&V> {
if let Some(value) = self.cache.get(key) {
self.access_times.insert(key.clone(), Instant::now());
Some(value)
} else {
None
}
}
fn evict_lru(&mut self) {
if let Some((oldest_key, _)) = self
.access_times
.iter()
.min_by_key(|(_, time)| *time)
.map(|(k, v)| (k.clone(), *v))
{
self.cache.remove(&oldest_key);
self.access_times.remove(&oldest_key);
}
}
pub fn clear(&mut self) {
self.cache.clear();
self.access_times.clear();
}
pub fn len(&self) -> usize {
self.cache.len()
}
pub fn is_empty(&self) -> bool {
self.cache.is_empty()
}
}
pub struct MemoryManager {
profiler: Arc<Mutex<MemoryProfiler>>,
performance_cache: Arc<Mutex<MemoryOptimizedCache<String, f64>>>,
animation_cache: Arc<Mutex<MemoryOptimizedCache<u64, String>>>,
max_memory_mb: f64,
}
impl MemoryManager {
pub fn new(max_memory_mb: f64) -> Self {
Self {
profiler: Arc::new(Mutex::new(MemoryProfiler::new())),
performance_cache: Arc::new(Mutex::new(MemoryOptimizedCache::new(1000))),
animation_cache: Arc::new(Mutex::new(MemoryOptimizedCache::new(500))),
max_memory_mb,
}
}
pub fn is_memory_ok(&self) -> bool {
if let Ok(profiler) = self.profiler.lock() {
profiler.get_peak_usage() < self.max_memory_mb
} else {
false
}
}
pub fn get_memory_usage(&self) -> f64 {
if let Ok(mut profiler) = self.profiler.lock() {
profiler.measure()
} else {
0.0
}
}
pub fn get_memory_trend(&self) -> MemoryTrend {
if let Ok(profiler) = self.profiler.lock() {
profiler.get_memory_trend()
} else {
MemoryTrend::Stable
}
}
pub fn force_gc(&self) {
if !self.is_memory_ok() {
if let Ok(mut cache) = self.performance_cache.lock() {
cache.clear();
}
if let Ok(mut cache) = self.animation_cache.lock() {
cache.clear();
}
}
}
pub fn cache_performance_metric(&self, key: String, value: f64) {
if let Ok(mut cache) = self.performance_cache.lock() {
cache.insert(key, value);
}
}
pub fn get_performance_metric(&self, key: &str) -> Option<f64> {
if let Ok(mut cache) = self.performance_cache.lock() {
cache.get(&key.to_string()).copied()
} else {
None
}
}
pub fn cache_animation_data(&self, id: u64, data: String) {
if let Ok(mut cache) = self.animation_cache.lock() {
cache.insert(id, data);
}
}
pub fn get_animation_data(&self, id: &u64) -> Option<String> {
if let Ok(mut cache) = self.animation_cache.lock() {
cache.get(id).cloned()
} else {
None
}
}
pub fn get_memory_stats(&self) -> MemoryStats {
let profiler = self.profiler.lock().unwrap();
let performance_cache = self.performance_cache.lock().unwrap();
let animation_cache = self.animation_cache.lock().unwrap();
MemoryStats {
peak_usage_mb: profiler.get_peak_usage(),
average_usage_mb: profiler.get_average_usage(),
trend: profiler.get_memory_trend(),
performance_cache_size: performance_cache.len(),
animation_cache_size: animation_cache.len(),
is_within_limits: self.is_memory_ok(),
}
}
}
#[derive(Debug, Clone)]
pub struct MemoryStats {
pub peak_usage_mb: f64,
pub average_usage_mb: f64,
pub trend: MemoryTrend,
pub performance_cache_size: usize,
pub animation_cache_size: usize,
pub is_within_limits: bool,
}
impl Default for MemoryManager {
fn default() -> Self {
Self::new(TARGET_MAX_MEMORY_MB)
}
}