(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
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.
### Maximizing

View file

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

View file

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

View file

@ -18,7 +18,13 @@ pub struct 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();
if get_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(
actually_get: bool,
) -> crate::utils::error::Result<Option<Vec<DiskHarvest>>> {
if !actually_get {
return Ok(None);
}
pub async fn get_disk_usage_list() -> crate::utils::error::Result<Vec<DiskHarvest>> {
let mut vec_disks: Vec<DiskHarvest> = Vec::new();
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));
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?;
Ok(MemHarvest {
Ok(Some(MemHarvest {
mem_total_in_mb: memory.total().get::<information::megabyte>(),
mem_used_in_mb: memory.total().get::<information::megabyte>()
- memory.available().get::<information::megabyte>(),
})
}))
}
pub async fn get_swap_data_list(
actually_get: bool,
) -> crate::utils::error::Result<Option<MemHarvest>> {
if !actually_get {
return Ok(None);
}
pub async fn get_swap_data_list() -> crate::utils::error::Result<MemHarvest> {
let memory = heim::memory::swap().await?;
Ok(MemHarvest {
Ok(Some(MemHarvest {
mem_total_in_mb: memory.total().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(
sys: &System, prev_net_access_time: Instant, prev_net_rx: &mut u64, prev_net_tx: &mut u64,
curr_time: Instant,
) -> NetworkHarvest {
curr_time: Instant, actually_get: bool,
) -> Option<NetworkHarvest> {
if !actually_get {
return None;
}
let mut io_data = net::io_counters();
let mut total_rx: 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_tx = total_tx;
NetworkHarvest {
Some(NetworkHarvest {
rx,
tx,
total_rx,
total_tx,
}
})
}

View file

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

View file

@ -864,7 +864,7 @@ pub struct BottomWidget {
pub flex_grow: bool,
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum BottomWidgetType {
Empty,
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::{
data_harvester::{self, processes::ProcessSorting},
layout_manager::UsedWidgets,
App,
};
use constants::*;
@ -139,6 +140,7 @@ fn main() -> error::Result<()> {
app.app_config_fields.update_rate_in_milliseconds,
app.app_config_fields.temperature_type.clone(),
app.app_config_fields.show_average_cpu,
app.used_widgets.clone(),
);
let mut painter = canvas::Painter::init(widget_layout);
@ -170,21 +172,29 @@ fn main() -> error::Result<()> {
// Convert all data into tui-compliant components
// Network
let network_data = convert_network_data_points(&app.data_collection, false);
if app.used_widgets.use_net {
let network_data =
convert_network_data_points(&app.data_collection, false);
app.canvas_data.network_data_rx = network_data.rx;
app.canvas_data.network_data_tx = network_data.tx;
app.canvas_data.rx_display = network_data.rx_display;
app.canvas_data.tx_display = network_data.tx_display;
app.canvas_data.total_rx_display = network_data.total_rx_display;
app.canvas_data.total_tx_display = network_data.total_tx_display;
}
// Disk
if app.used_widgets.use_disk {
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 =
@ -192,8 +202,10 @@ fn main() -> error::Result<()> {
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| {
@ -207,14 +219,17 @@ fn main() -> error::Result<()> {
// CPU
app.canvas_data.cpu_data =
convert_cpu_data_points(&app.data_collection, false);
}
// Processes
if app.used_widgets.use_proc {
let (single, grouped) = convert_process_data(&app.data_collection);
app.canvas_data.process_data = single;
app.canvas_data.grouped_process_data = grouped;
update_all_process_lists(&mut app);
}
}
}
BottomEvent::Clean => {
app.data_collection
.clean_data(constants::STALE_MAX_MILLISECONDS);
@ -743,15 +758,16 @@ fn create_event_thread(
>,
rrx: std::sync::mpsc::Receiver<ResetEvent>, use_current_cpu_total: bool,
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 || {
let tx = tx.clone();
let mut data_state = data_harvester::DataState::default();
data_state.init();
let mut data_state = data_harvester::DataCollector::default();
data_state.set_collected_data(used_widget_set);
data_state.set_temperature_type(temp_type);
data_state.set_use_current_cpu_total(use_current_cpu_total);
data_state.set_show_average_cpu(show_average_cpu);
data_state.init();
loop {
if let Ok(message) = rrx.try_recv() {
match message {

View file

@ -1,5 +1,5 @@
use serde::Deserialize;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::time::Instant;
use crate::{
@ -100,6 +100,8 @@ pub fn build_app(
let mut initial_widget_type = BottomWidgetType::Proc;
let is_custom_layout = config.row.is_some();
let mut used_widget_set = HashSet::new();
for row in &widget_layout.rows {
for col in &row.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 {
BottomWidgetType::Cpu => {
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 {
Some(match initial_widget_type {
BottomWidgetType::Proc | BottomWidgetType::Disk | BottomWidgetType::Temp => {
@ -213,6 +217,18 @@ pub fn build_app(
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()
.app_config_fields(app_config_fields)
.cpu_state(CpuState::init(cpu_state_map))
@ -224,6 +240,7 @@ pub fn build_app(
.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
.widget_map(widget_map)
.used_widgets(used_widgets)
.build())
}