mirror of
https://github.com/ClementTsang/bottom
synced 2024-11-23 04:33:10 +00:00
(perf) Avoid harvesting if widget is not being displayed
This commit is contained in:
parent
620d614f12
commit
ba0fbf808e
10 changed files with 217 additions and 105 deletions
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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>(),
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -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 {
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue