Skip to main content

quorum_lib/commands/
device_performance.rs

1use std::sync::{Arc, LazyLock, Mutex};
2use std::time::{Duration, Instant};
3use sysinfo::{Components, System};
4
5struct CachedPerformanceTier {
6    tier: String,
7    last_updated: Instant,
8}
9
10static CACHED_TIER: LazyLock<Arc<Mutex<CachedPerformanceTier>>> = LazyLock::new(|| {
11    Arc::new(Mutex::new(CachedPerformanceTier {
12        tier: "medium".to_string(),
13        last_updated: Instant::now() - Duration::from_secs(10),
14    }))
15});
16
17/// Used to get an understanding of device power
18///
19/// The function gets various system information (see below) and then calculates
20/// a score which returns "high", "medium" or "low".
21/// This function has many uses but is ideal for tweaking settings or interal things to
22/// operate on the user's device more effeciently.
23///
24/// This is dynamic in a way that, if a "high" rated device has low system resources available (such that a heavy operation could
25/// cause the device to freeze or "lag"), this function will not return "high" even though the device is capable of it.
26/// This is to ensure that the user has a good experience and does not have to deal with a "laggy" application due to quantity of resouces.
27///
28/// Below is what system information this function looks at
29/// - CPU Core count
30/// - CPU frequency in MHz
31/// - Total RAM/memory
32/// - Used RAM/memory
33/// - System load (1-minute average)
34///
35/// # returns
36/// - "high" if the device is capable of running heavy operations without lagging
37/// - "medium" if the device is capable of running medium operations without lagging
38/// - "low" if the device is not capable of running medium or heavy operations without lagging
39///
40/// # Example
41/// ```rust
42/// let performance_tier = get_performance_tier().await.unwrap();
43/// assert!(performance_tier == "high" || performance_tier == "medium" || performance_tier == "low");
44/// ```
45#[tauri::command]
46pub async fn get_performance_tier() -> Result<String, String> {
47    //Keep a cached result for 1 minute so each call isn't constantly calculating all the time
48    let mut cached = CACHED_TIER.lock().unwrap();
49    if cached.last_updated.elapsed() < Duration::from_secs(60) {
50        return Ok(cached.tier.clone());
51    }
52
53    let mut system = System::new_all();
54    system.refresh_all();
55
56    // CPU information
57    let cpu_cores = system.cpus().len();
58    let cpu_speed_mhz = system.cpus()[0].frequency();
59
60    // Memory Information
61    let total_memory_mb = system.total_memory() / (1024 * 1024);
62    let used_memory_mb = system.used_memory() / (1024 * 1024);
63
64    // Swap Information
65    let total_swap_mb = system.total_swap() / (1024 * 1024);
66    let used_swap_mb = system.used_swap() / (1024 * 1024);
67    let swap_usage_percentage = if total_swap_mb > 0 {
68        (used_swap_mb as f64 / total_swap_mb as f64) * 100.0
69    } else {
70        0.0
71    };
72
73    // System load (1-minute average)
74    let load_avg = System::load_average().one;
75
76    //Components temperatures
77    let components = Components::new_with_refreshed_list();
78    let max_cpu_temp = components
79        .iter()
80        .filter(|c| c.label().to_lowercase().contains("cpu"))
81        .filter_map(|c| c.temperature())
82        .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
83        .unwrap_or(0.0);
84
85    // Calculate a performance score
86    let temp_penalty = if max_cpu_temp > 80.0 {
87        0.5
88    }
89    // High temperature
90    else if max_cpu_temp > 60.0 {
91        0.2
92    }
93    // Moderate temperature
94    else {
95        0.0
96    }; // No penalty
97    let cpu_score = if cpu_cores >= 8 {
98        3.0 - temp_penalty
99    } else if cpu_cores >= 4 {
100        2.0 - temp_penalty
101    } else {
102        1.0 - temp_penalty
103    };
104    let cpu_speed_score = if cpu_speed_mhz >= 3000 {
105        3.0
106    } else if cpu_speed_mhz >= 2000 {
107        2.0
108    } else {
109        1.0
110    };
111    let available_memory_mb = total_memory_mb - used_memory_mb;
112    let swap_penalty = if swap_usage_percentage > 50.0 {
113        0.5
114    } else if swap_usage_percentage > 20.0 {
115        0.2
116    } else {
117        0.0
118    };
119    let memory_score = if available_memory_mb >= 12_000 {
120        3.0 - swap_penalty
121    } else if available_memory_mb >= 6_000 {
122        2.0 - swap_penalty
123    } else {
124        1.0 - swap_penalty
125    };
126    let load_score = if load_avg < 1.0 {
127        3.0
128    } else if load_avg < 2.0 {
129        2.0
130    } else {
131        1.0
132    };
133
134    // Weighted score
135    let performance_score =
136        (cpu_score * 0.3) + (cpu_speed_score * 0.2) + (memory_score * 0.3) + (load_score * 0.2);
137
138    // Determine tier
139    let tier = if performance_score >= 2.5 {
140        "high"
141    } else if performance_score >= 1.5 {
142        "medium"
143    } else {
144        "low"
145    };
146
147    cached.tier = tier.to_string();
148    cached.last_updated = Instant::now();
149    Ok(tier.to_string())
150}