(perf) Avoid harvesting if widget is not being displayed

This commit is contained in:
Clement Tsang 2020-04-04 18:29:32 -04:00 committed by GitHub
parent 620d614f12
commit ba0fbf808e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 217 additions and 105 deletions

View file

@ -213,7 +213,7 @@ searching by PID and by process name.
### Zoom ### Zoom
Using the +/- keys or the scroll wheel will move adjust the current time intervals of the currently selected widget. Widgets Using the `+`/`-` keys or the scroll wheel will move adjust the current time intervals of the currently selected widget. Widgets
can hold different time intervals independently. can hold different time intervals independently.
### Maximizing ### Maximizing

View file

@ -512,6 +512,7 @@ pub struct App {
pub app_config_fields: AppConfigFields, pub app_config_fields: AppConfigFields,
pub widget_map: HashMap<u64, BottomWidget>, pub widget_map: HashMap<u64, BottomWidget>,
pub current_widget: BottomWidget, pub current_widget: BottomWidget,
pub used_widgets: UsedWidgets,
} }
impl App { impl App {

View file

@ -4,6 +4,8 @@ use std::{collections::HashMap, time::Instant};
use sysinfo::{System, SystemExt}; use sysinfo::{System, SystemExt};
use crate::app::layout_manager::UsedWidgets;
use futures::join; use futures::join;
pub mod cpu; pub mod cpu;
@ -56,7 +58,7 @@ impl Data {
} }
} }
pub struct DataState { pub struct DataCollector {
pub data: Data, pub data: Data,
sys: System, sys: System,
prev_pid_stats: HashMap<String, (f64, Instant)>, prev_pid_stats: HashMap<String, (f64, Instant)>,
@ -69,11 +71,12 @@ pub struct DataState {
total_rx: u64, total_rx: u64,
total_tx: u64, total_tx: u64,
show_average_cpu: bool, show_average_cpu: bool,
widgets_to_harvest: UsedWidgets,
} }
impl Default for DataState { impl Default for DataCollector {
fn default() -> Self { fn default() -> Self {
DataState { DataCollector {
data: Data::default(), data: Data::default(),
sys: System::new_all(), sys: System::new_all(),
prev_pid_stats: HashMap::new(), prev_pid_stats: HashMap::new(),
@ -86,11 +89,23 @@ impl Default for DataState {
total_rx: 0, total_rx: 0,
total_tx: 0, total_tx: 0,
show_average_cpu: false, show_average_cpu: false,
widgets_to_harvest: UsedWidgets::default(),
} }
} }
} }
impl DataState { impl DataCollector {
pub fn init(&mut self) {
self.mem_total_kb = self.sys.get_total_memory();
futures::executor::block_on(self.update_data());
std::thread::sleep(std::time::Duration::from_millis(250));
self.data.first_run_cleanup();
}
pub fn set_collected_data(&mut self, used_widgets: UsedWidgets) {
self.widgets_to_harvest = used_widgets;
}
pub fn set_temperature_type(&mut self, temperature_type: temperature::TemperatureType) { pub fn set_temperature_type(&mut self, temperature_type: temperature::TemperatureType) {
self.temperature_type = temperature_type; self.temperature_type = temperature_type;
} }
@ -103,42 +118,45 @@ impl DataState {
self.show_average_cpu = show_average_cpu; self.show_average_cpu = show_average_cpu;
} }
pub fn init(&mut self) {
self.mem_total_kb = self.sys.get_total_memory();
futures::executor::block_on(self.update_data());
std::thread::sleep(std::time::Duration::from_millis(250));
self.data.first_run_cleanup();
}
pub async fn update_data(&mut self) { pub async fn update_data(&mut self) {
self.sys.refresh_system(); if self.widgets_to_harvest.use_cpu {
self.sys.refresh_cpu();
}
if cfg!(not(target_os = "linux")) { if cfg!(not(target_os = "linux")) {
self.sys.refresh_processes(); if self.widgets_to_harvest.use_proc {
self.sys.refresh_components(); self.sys.refresh_processes();
}
if self.widgets_to_harvest.use_temp {
self.sys.refresh_components();
}
} }
if cfg!(target_os = "windows") { if cfg!(target_os = "windows") && self.widgets_to_harvest.use_net {
self.sys.refresh_networks(); self.sys.refresh_networks();
} }
let current_instant = std::time::Instant::now(); let current_instant = std::time::Instant::now();
// CPU // CPU
self.data.cpu = cpu::get_cpu_data_list(&self.sys, self.show_average_cpu); if self.widgets_to_harvest.use_cpu {
self.data.cpu = cpu::get_cpu_data_list(&self.sys, self.show_average_cpu);
}
// Processes. This is the longest part of the harvesting process... changing this might be if self.widgets_to_harvest.use_proc {
// good in the future. What was tried already: // Processes. This is the longest part of the harvesting process... changing this might be
// * Splitting the internal part into multiple scoped threads (dropped by ~.01 seconds, but upped usage) // good in the future. What was tried already:
if let Ok(process_list) = processes::get_sorted_processes_list( // * Splitting the internal part into multiple scoped threads (dropped by ~.01 seconds, but upped usage)
&self.sys, if let Ok(process_list) = processes::get_sorted_processes_list(
&mut self.prev_idle, &self.sys,
&mut self.prev_non_idle, &mut self.prev_idle,
&mut self.prev_pid_stats, &mut self.prev_non_idle,
self.use_current_cpu_total, &mut self.prev_pid_stats,
self.mem_total_kb, self.use_current_cpu_total,
current_instant, self.mem_total_kb,
) { current_instant,
self.data.list_of_processes = process_list; ) {
self.data.list_of_processes = process_list;
}
} }
// ASYNC // ASYNC
@ -148,13 +166,18 @@ impl DataState {
&mut self.total_rx, &mut self.total_rx,
&mut self.total_tx, &mut self.total_tx,
current_instant, current_instant,
self.widgets_to_harvest.use_net,
); );
let mem_data_fut = mem::get_mem_data_list(); let mem_data_fut = mem::get_mem_data_list(self.widgets_to_harvest.use_mem);
let swap_data_fut = mem::get_swap_data_list(); let swap_data_fut = mem::get_swap_data_list(self.widgets_to_harvest.use_mem);
let disk_data_fut = disks::get_disk_usage_list(); let disk_data_fut = disks::get_disk_usage_list(self.widgets_to_harvest.use_disk);
let disk_io_usage_fut = disks::get_io_usage_list(false); let disk_io_usage_fut = disks::get_io_usage_list(false, self.widgets_to_harvest.use_disk);
let temp_data_fut = temperature::get_temperature_data(&self.sys, &self.temperature_type); let temp_data_fut = temperature::get_temperature_data(
&self.sys,
&self.temperature_type,
self.widgets_to_harvest.use_temp,
);
let (net_data, mem_res, swap_res, disk_res, io_res, temp_res) = join!( let (net_data, mem_res, swap_res, disk_res, io_res, temp_res) = join!(
network_data_fut, network_data_fut,
@ -166,27 +189,40 @@ impl DataState {
); );
// After async // After async
self.data.network = net_data; if let Some(net_data) = net_data {
self.total_rx = self.data.network.total_rx; self.data.network = net_data;
self.total_tx = self.data.network.total_tx; self.total_rx = self.data.network.total_rx;
self.total_tx = self.data.network.total_tx;
}
if let Ok(memory) = mem_res { if let Ok(memory) = mem_res {
self.data.memory = memory; if let Some(memory) = memory {
self.data.memory = memory;
}
} }
if let Ok(swap) = swap_res { if let Ok(swap) = swap_res {
self.data.swap = swap; if let Some(swap) = swap {
self.data.swap = swap;
}
} }
if let Ok(disks) = disk_res { if let Ok(disks) = disk_res {
self.data.disks = disks; if let Some(disks) = disks {
self.data.disks = disks;
}
} }
if let Ok(io) = io_res { if let Ok(io) = io_res {
self.data.io = io; if let Some(io) = io {
self.data.io = io;
}
} }
if let Ok(temp) = temp_res { if let Ok(temp) = temp_res {
self.data.temperature_sensors = temp; if let Some(temp) = temp {
self.data.temperature_sensors = temp;
}
} }
// Update time // Update time

View file

@ -18,7 +18,13 @@ pub struct IOData {
pub type IOHarvest = std::collections::HashMap<String, IOData>; pub type IOHarvest = std::collections::HashMap<String, IOData>;
pub async fn get_io_usage_list(get_physical: bool) -> crate::utils::error::Result<IOHarvest> { pub async fn get_io_usage_list(
get_physical: bool, actually_get: bool,
) -> crate::utils::error::Result<Option<IOHarvest>> {
if !actually_get {
return Ok(None);
}
let mut io_hash: std::collections::HashMap<String, IOData> = std::collections::HashMap::new(); let mut io_hash: std::collections::HashMap<String, IOData> = std::collections::HashMap::new();
if get_physical { if get_physical {
let mut physical_counter_stream = heim::disk::io_counters_physical(); let mut physical_counter_stream = heim::disk::io_counters_physical();
@ -48,10 +54,16 @@ pub async fn get_io_usage_list(get_physical: bool) -> crate::utils::error::Resul
} }
} }
Ok(io_hash) Ok(Some(io_hash))
} }
pub async fn get_disk_usage_list() -> crate::utils::error::Result<Vec<DiskHarvest>> { pub async fn get_disk_usage_list(
actually_get: bool,
) -> crate::utils::error::Result<Option<Vec<DiskHarvest>>> {
if !actually_get {
return Ok(None);
}
let mut vec_disks: Vec<DiskHarvest> = Vec::new(); let mut vec_disks: Vec<DiskHarvest> = Vec::new();
let mut partitions_stream = heim::disk::partitions_physical(); let mut partitions_stream = heim::disk::partitions_physical();
@ -81,5 +93,5 @@ pub async fn get_disk_usage_list() -> crate::utils::error::Result<Vec<DiskHarves
vec_disks.sort_by(|a, b| a.name.cmp(&b.name)); vec_disks.sort_by(|a, b| a.name.cmp(&b.name));
Ok(vec_disks) Ok(Some(vec_disks))
} }

View file

@ -15,21 +15,33 @@ impl Default for MemHarvest {
} }
} }
pub async fn get_mem_data_list() -> crate::utils::error::Result<MemHarvest> { pub async fn get_mem_data_list(
actually_get: bool,
) -> crate::utils::error::Result<Option<MemHarvest>> {
if !actually_get {
return Ok(None);
}
let memory = heim::memory::memory().await?; let memory = heim::memory::memory().await?;
Ok(MemHarvest { Ok(Some(MemHarvest {
mem_total_in_mb: memory.total().get::<information::megabyte>(), mem_total_in_mb: memory.total().get::<information::megabyte>(),
mem_used_in_mb: memory.total().get::<information::megabyte>() mem_used_in_mb: memory.total().get::<information::megabyte>()
- memory.available().get::<information::megabyte>(), - memory.available().get::<information::megabyte>(),
}) }))
} }
pub async fn get_swap_data_list() -> crate::utils::error::Result<MemHarvest> { pub async fn get_swap_data_list(
actually_get: bool,
) -> crate::utils::error::Result<Option<MemHarvest>> {
if !actually_get {
return Ok(None);
}
let memory = heim::memory::swap().await?; let memory = heim::memory::swap().await?;
Ok(MemHarvest { Ok(Some(MemHarvest {
mem_total_in_mb: memory.total().get::<information::megabyte>(), mem_total_in_mb: memory.total().get::<information::megabyte>(),
mem_used_in_mb: memory.used().get::<information::megabyte>(), mem_used_in_mb: memory.used().get::<information::megabyte>(),
}) }))
} }

View file

@ -22,8 +22,12 @@ impl NetworkHarvest {
pub async fn get_network_data( pub async fn get_network_data(
sys: &System, prev_net_access_time: Instant, prev_net_rx: &mut u64, prev_net_tx: &mut u64, sys: &System, prev_net_access_time: Instant, prev_net_rx: &mut u64, prev_net_tx: &mut u64,
curr_time: Instant, curr_time: Instant, actually_get: bool,
) -> NetworkHarvest { ) -> Option<NetworkHarvest> {
if !actually_get {
return None;
}
let mut io_data = net::io_counters(); let mut io_data = net::io_counters();
let mut total_rx: u64 = 0; let mut total_rx: u64 = 0;
let mut total_tx: u64 = 0; let mut total_tx: u64 = 0;
@ -56,10 +60,10 @@ pub async fn get_network_data(
*prev_net_rx = total_rx; *prev_net_rx = total_rx;
*prev_net_tx = total_tx; *prev_net_tx = total_tx;
NetworkHarvest { Some(NetworkHarvest {
rx, rx,
tx, tx,
total_rx, total_rx,
total_tx, total_tx,
} })
} }

View file

@ -24,8 +24,12 @@ impl Default for TemperatureType {
} }
pub async fn get_temperature_data( pub async fn get_temperature_data(
sys: &System, temp_type: &TemperatureType, sys: &System, temp_type: &TemperatureType, actually_get: bool,
) -> crate::utils::error::Result<Vec<TempHarvest>> { ) -> crate::utils::error::Result<Option<Vec<TempHarvest>>> {
if !actually_get {
return Ok(None);
}
let mut temperature_vec: Vec<TempHarvest> = Vec::new(); let mut temperature_vec: Vec<TempHarvest> = Vec::new();
if cfg!(target_os = "linux") { if cfg!(target_os = "linux") {
@ -86,7 +90,7 @@ pub async fn get_temperature_data(
.unwrap_or(Ordering::Equal) .unwrap_or(Ordering::Equal)
}); });
Ok(temperature_vec) Ok(Some(temperature_vec))
} }
fn convert_celsius_to_kelvin(celsius: f32) -> f32 { fn convert_celsius_to_kelvin(celsius: f32) -> f32 {

View file

@ -864,7 +864,7 @@ pub struct BottomWidget {
pub flex_grow: bool, pub flex_grow: bool,
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum BottomWidgetType { pub enum BottomWidgetType {
Empty, Empty,
Cpu, Cpu,
@ -938,3 +938,13 @@ impl std::str::FromStr for BottomWidgetType {
} }
} }
} }
#[derive(Clone, Default)]
pub struct UsedWidgets {
pub use_cpu: bool,
pub use_mem: bool,
pub use_net: bool,
pub use_proc: bool,
pub use_disk: bool,
pub use_temp: bool,
}

View file

@ -27,6 +27,7 @@ use tui::{backend::CrosstermBackend, Terminal};
use app::{ use app::{
data_harvester::{self, processes::ProcessSorting}, data_harvester::{self, processes::ProcessSorting},
layout_manager::UsedWidgets,
App, App,
}; };
use constants::*; use constants::*;
@ -139,6 +140,7 @@ fn main() -> error::Result<()> {
app.app_config_fields.update_rate_in_milliseconds, app.app_config_fields.update_rate_in_milliseconds,
app.app_config_fields.temperature_type.clone(), app.app_config_fields.temperature_type.clone(),
app.app_config_fields.show_average_cpu, app.app_config_fields.show_average_cpu,
app.used_widgets.clone(),
); );
let mut painter = canvas::Painter::init(widget_layout); let mut painter = canvas::Painter::init(widget_layout);
@ -170,49 +172,62 @@ fn main() -> error::Result<()> {
// Convert all data into tui-compliant components // Convert all data into tui-compliant components
// Network // Network
let network_data = convert_network_data_points(&app.data_collection, false); if app.used_widgets.use_net {
app.canvas_data.network_data_rx = network_data.rx; let network_data =
app.canvas_data.network_data_tx = network_data.tx; convert_network_data_points(&app.data_collection, false);
app.canvas_data.rx_display = network_data.rx_display; app.canvas_data.network_data_rx = network_data.rx;
app.canvas_data.tx_display = network_data.tx_display; app.canvas_data.network_data_tx = network_data.tx;
app.canvas_data.total_rx_display = network_data.total_rx_display; app.canvas_data.rx_display = network_data.rx_display;
app.canvas_data.total_tx_display = network_data.total_tx_display; app.canvas_data.tx_display = network_data.tx_display;
app.canvas_data.total_rx_display = network_data.total_rx_display;
// Disk app.canvas_data.total_tx_display = network_data.total_tx_display;
app.canvas_data.disk_data = convert_disk_row(&app.data_collection);
// Temperatures
app.canvas_data.temp_sensor_data = convert_temp_row(&app);
// Memory
app.canvas_data.mem_data =
convert_mem_data_points(&app.data_collection, false);
app.canvas_data.swap_data =
convert_swap_data_points(&app.data_collection, false);
let memory_and_swap_labels = convert_mem_labels(&app.data_collection);
app.canvas_data.mem_label = memory_and_swap_labels.0;
app.canvas_data.swap_label = memory_and_swap_labels.1;
// Pre-fill CPU if needed
if first_run {
let cpu_len = app.data_collection.cpu_harvest.len();
app.cpu_state.widget_states.values_mut().for_each(|state| {
state.core_show_vec = vec![true; cpu_len];
state.num_cpus_shown = cpu_len;
});
app.cpu_state.num_cpus_total = cpu_len;
first_run = false;
} }
// CPU // Disk
app.canvas_data.cpu_data = if app.used_widgets.use_disk {
convert_cpu_data_points(&app.data_collection, false); app.canvas_data.disk_data = convert_disk_row(&app.data_collection);
}
// Temperatures
if app.used_widgets.use_temp {
app.canvas_data.temp_sensor_data = convert_temp_row(&app);
}
// Memory
if app.used_widgets.use_mem {
app.canvas_data.mem_data =
convert_mem_data_points(&app.data_collection, false);
app.canvas_data.swap_data =
convert_swap_data_points(&app.data_collection, false);
let memory_and_swap_labels = convert_mem_labels(&app.data_collection);
app.canvas_data.mem_label = memory_and_swap_labels.0;
app.canvas_data.swap_label = memory_and_swap_labels.1;
}
// Pre-fill CPU if needed
if app.used_widgets.use_cpu {
if first_run {
let cpu_len = app.data_collection.cpu_harvest.len();
app.cpu_state.widget_states.values_mut().for_each(|state| {
state.core_show_vec = vec![true; cpu_len];
state.num_cpus_shown = cpu_len;
});
app.cpu_state.num_cpus_total = cpu_len;
first_run = false;
}
// CPU
app.canvas_data.cpu_data =
convert_cpu_data_points(&app.data_collection, false);
}
// Processes // Processes
let (single, grouped) = convert_process_data(&app.data_collection); if app.used_widgets.use_proc {
app.canvas_data.process_data = single; let (single, grouped) = convert_process_data(&app.data_collection);
app.canvas_data.grouped_process_data = grouped; app.canvas_data.process_data = single;
update_all_process_lists(&mut app); app.canvas_data.grouped_process_data = grouped;
update_all_process_lists(&mut app);
}
} }
} }
BottomEvent::Clean => { BottomEvent::Clean => {
@ -743,15 +758,16 @@ fn create_event_thread(
>, >,
rrx: std::sync::mpsc::Receiver<ResetEvent>, use_current_cpu_total: bool, rrx: std::sync::mpsc::Receiver<ResetEvent>, use_current_cpu_total: bool,
update_rate_in_milliseconds: u64, temp_type: data_harvester::temperature::TemperatureType, update_rate_in_milliseconds: u64, temp_type: data_harvester::temperature::TemperatureType,
show_average_cpu: bool, show_average_cpu: bool, used_widget_set: UsedWidgets,
) { ) {
thread::spawn(move || { thread::spawn(move || {
let tx = tx.clone(); let tx = tx.clone();
let mut data_state = data_harvester::DataState::default(); let mut data_state = data_harvester::DataCollector::default();
data_state.init(); data_state.set_collected_data(used_widget_set);
data_state.set_temperature_type(temp_type); data_state.set_temperature_type(temp_type);
data_state.set_use_current_cpu_total(use_current_cpu_total); data_state.set_use_current_cpu_total(use_current_cpu_total);
data_state.set_show_average_cpu(show_average_cpu); data_state.set_show_average_cpu(show_average_cpu);
data_state.init();
loop { loop {
if let Ok(message) = rrx.try_recv() { if let Ok(message) = rrx.try_recv() {
match message { match message {

View file

@ -1,5 +1,5 @@
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::time::Instant; use std::time::Instant;
use crate::{ use crate::{
@ -100,6 +100,8 @@ pub fn build_app(
let mut initial_widget_type = BottomWidgetType::Proc; let mut initial_widget_type = BottomWidgetType::Proc;
let is_custom_layout = config.row.is_some(); let is_custom_layout = config.row.is_some();
let mut used_widget_set = HashSet::new();
for row in &widget_layout.rows { for row in &widget_layout.rows {
for col in &row.children { for col in &row.children {
for col_row in &col.children { for col_row in &col.children {
@ -135,6 +137,9 @@ pub fn build_app(
} }
} }
} }
used_widget_set.insert(widget.widget_type.clone());
match widget.widget_type { match widget.widget_type {
BottomWidgetType::Cpu => { BottomWidgetType::Cpu => {
cpu_state_map.insert( cpu_state_map.insert(
@ -178,7 +183,6 @@ pub fn build_app(
} }
} }
// FIXME: [MODULARITY] Don't collect if not added!
let basic_table_widget_state = if use_basic_mode { let basic_table_widget_state = if use_basic_mode {
Some(match initial_widget_type { Some(match initial_widget_type {
BottomWidgetType::Proc | BottomWidgetType::Disk | BottomWidgetType::Temp => { BottomWidgetType::Proc | BottomWidgetType::Disk | BottomWidgetType::Temp => {
@ -213,6 +217,18 @@ pub fn build_app(
autohide_time, autohide_time,
}; };
let used_widgets = UsedWidgets {
use_cpu: used_widget_set.get(&BottomWidgetType::Cpu).is_some()
|| used_widget_set.get(&BottomWidgetType::BasicCpu).is_some(),
use_mem: used_widget_set.get(&BottomWidgetType::Mem).is_some()
|| used_widget_set.get(&BottomWidgetType::BasicMem).is_some(),
use_net: used_widget_set.get(&BottomWidgetType::Net).is_some()
|| used_widget_set.get(&BottomWidgetType::BasicNet).is_some(),
use_proc: used_widget_set.get(&BottomWidgetType::Proc).is_some(),
use_disk: used_widget_set.get(&BottomWidgetType::Disk).is_some(),
use_temp: used_widget_set.get(&BottomWidgetType::Temp).is_some(),
};
Ok(App::builder() Ok(App::builder()
.app_config_fields(app_config_fields) .app_config_fields(app_config_fields)
.cpu_state(CpuState::init(cpu_state_map)) .cpu_state(CpuState::init(cpu_state_map))
@ -224,6 +240,7 @@ pub fn build_app(
.basic_table_widget_state(basic_table_widget_state) .basic_table_widget_state(basic_table_widget_state)
.current_widget(widget_map.get(&initial_widget_id).unwrap().clone()) // I think the unwrap is fine here .current_widget(widget_map.get(&initial_widget_id).unwrap().clone()) // I think the unwrap is fine here
.widget_map(widget_map) .widget_map(widget_map)
.used_widgets(used_widgets)
.build()) .build())
} }