refactor: use nonzero in mem data (#1673)

* refactor: use nonzerou64 for mem data

* clippy

* comment
This commit is contained in:
Clement Tsang 2025-02-12 00:58:15 -05:00 committed by GitHub
parent 22fbd7d630
commit 702775f58d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 124 additions and 141 deletions

View File

@ -7,7 +7,7 @@ use std::{
use crate::collection::batteries;
use crate::{
app::AppConfigFields,
collection::{cpu, disks, memory::MemHarvest, network, Data},
collection::{cpu, disks, memory::MemData, network, Data},
dec_bytes_per_second_string,
utils::data_units::DataUnit,
widgets::{DiskWidgetData, TempWidgetData},
@ -23,14 +23,14 @@ pub struct StoredData {
pub last_update_time: Instant, // FIXME: (points_rework_v1) remove this?
pub timeseries_data: TimeSeriesData, // FIXME: (points_rework_v1) Skip in basic?
pub network_harvest: network::NetworkHarvest,
pub ram_harvest: MemHarvest,
pub swap_harvest: Option<MemHarvest>,
pub ram_harvest: Option<MemData>,
pub swap_harvest: Option<MemData>,
#[cfg(not(target_os = "windows"))]
pub cache_harvest: Option<MemHarvest>,
pub cache_harvest: Option<MemData>,
#[cfg(feature = "zfs")]
pub arc_harvest: Option<MemHarvest>,
pub arc_harvest: Option<MemData>,
#[cfg(feature = "gpu")]
pub gpu_harvest: Vec<(String, MemHarvest)>,
pub gpu_harvest: Vec<(String, MemData)>,
pub cpu_harvest: cpu::CpuHarvest,
pub load_avg_harvest: cpu::LoadAvgHarvest,
pub process_data: ProcessData,
@ -48,7 +48,7 @@ impl Default for StoredData {
last_update_time: Instant::now(),
timeseries_data: TimeSeriesData::default(),
network_harvest: network::NetworkHarvest::default(),
ram_harvest: MemHarvest::default(),
ram_harvest: None,
#[cfg(not(target_os = "windows"))]
cache_harvest: None,
swap_harvest: None,
@ -96,10 +96,7 @@ impl StoredData {
self.network_harvest = network;
}
if let Some(memory) = data.memory {
self.ram_harvest = memory;
}
self.ram_harvest = data.memory;
self.swap_harvest = data.swap;
#[cfg(not(target_os = "windows"))]

View File

@ -99,13 +99,13 @@ impl TimeSeriesData {
}
if let Some(memory) = &data.memory {
self.ram.try_push(memory.checked_percent());
self.ram.push(memory.percentage());
} else {
self.ram.insert_break();
}
if let Some(swap) = &data.swap {
self.swap.try_push(swap.checked_percent());
self.swap.push(swap.percentage());
} else {
self.swap.insert_break();
}
@ -113,7 +113,7 @@ impl TimeSeriesData {
#[cfg(not(target_os = "windows"))]
{
if let Some(cache) = &data.cache {
self.cache_mem.try_push(cache.checked_percent());
self.cache_mem.push(cache.percentage());
} else {
self.cache_mem.insert_break();
}
@ -122,7 +122,7 @@ impl TimeSeriesData {
#[cfg(feature = "zfs")]
{
if let Some(arc) = &data.arc {
self.arc_mem.try_push(arc.checked_percent());
self.arc_mem.push(arc.percentage());
} else {
self.arc_mem.insert_break();
}
@ -149,7 +149,7 @@ impl TimeSeriesData {
.gpu_mem
.get_mut(name)
.expect("entry must exist as it was created above");
curr.try_push(new_data.checked_percent());
curr.push(new_data.percentage());
}
for nv in not_visited {

View File

@ -8,37 +8,31 @@ use tui::{
use crate::{
app::App,
canvas::{components::pipe_gauge::PipeGauge, drawing_utils::widget_block, Painter},
collection::memory::MemHarvest,
collection::memory::MemData,
get_binary_unit_and_denominator,
};
/// Convert memory info into a string representing a fraction.
#[inline]
fn memory_fraction_label(data: &MemHarvest) -> Cow<'static, str> {
if data.total_bytes > 0 {
let (unit, denominator) = get_binary_unit_and_denominator(data.total_bytes);
let used = data.used_bytes as f64 / denominator;
let total = data.total_bytes as f64 / denominator;
fn memory_fraction_label(data: &MemData) -> Cow<'static, str> {
let total_bytes = data.total_bytes.get();
let (unit, denominator) = get_binary_unit_and_denominator(total_bytes);
let used = data.used_bytes as f64 / denominator;
let total = total_bytes as f64 / denominator;
format!("{used:.1}{unit}/{total:.1}{unit}").into()
} else {
"0.0B/0.0B".into()
}
format!("{used:.1}{unit}/{total:.1}{unit}").into()
}
/// Convert memory info into a string representing a percentage.
#[inline]
fn memory_percentage_label(data: &MemHarvest) -> Cow<'static, str> {
if data.total_bytes > 0 {
let percentage = data.used_bytes as f64 / data.total_bytes as f64 * 100.0;
format!("{percentage:3.0}%").into()
} else {
" 0%".into()
}
fn memory_percentage_label(data: &MemData) -> Cow<'static, str> {
let total_bytes = data.total_bytes.get();
let percentage = data.used_bytes as f64 / total_bytes as f64 * 100.0;
format!("{percentage:3.0}%").into()
}
#[inline]
fn memory_label(data: &MemHarvest, is_percentage: bool) -> Cow<'static, str> {
fn memory_label(data: &MemData, is_percentage: bool) -> Cow<'static, str> {
if is_percentage {
memory_percentage_label(data)
} else {
@ -62,8 +56,21 @@ impl Painter {
let data = app_state.data_store.get_data();
let ram_percentage = data.ram_harvest.saturating_percentage();
let ram_label = memory_label(&data.ram_harvest, app_state.basic_mode_use_percent);
let (ram_percentage, ram_label) = if let Some(ram_harvest) = &data.ram_harvest {
(
ram_harvest.percentage(),
memory_label(ram_harvest, app_state.basic_mode_use_percent),
)
} else {
(
0.0,
if app_state.basic_mode_use_percent {
"0.0B/0.0B".into()
} else {
" 0%".into()
},
)
};
draw_widgets.push(
PipeGauge::default()
@ -75,7 +82,7 @@ impl Painter {
);
if let Some(swap_harvest) = &data.swap_harvest {
let swap_percentage = swap_harvest.saturating_percentage();
let swap_percentage = swap_harvest.percentage();
let swap_label = memory_label(swap_harvest, app_state.basic_mode_use_percent);
draw_widgets.push(
@ -91,7 +98,7 @@ impl Painter {
#[cfg(not(target_os = "windows"))]
{
if let Some(cache_harvest) = &data.cache_harvest {
let cache_percentage = cache_harvest.saturating_percentage();
let cache_percentage = cache_harvest.percentage();
let cache_fraction_label =
memory_label(cache_harvest, app_state.basic_mode_use_percent);
@ -109,7 +116,7 @@ impl Painter {
#[cfg(feature = "zfs")]
{
if let Some(arc_harvest) = &data.arc_harvest {
let arc_percentage = arc_harvest.saturating_percentage();
let arc_percentage = arc_harvest.percentage();
let arc_fraction_label =
memory_label(arc_harvest, app_state.basic_mode_use_percent);
@ -130,7 +137,7 @@ impl Painter {
let mut colour_index = 0;
for (_, harvest) in data.gpu_harvest.iter() {
let percentage = harvest.saturating_percentage();
let percentage = harvest.percentage();
let label = memory_label(harvest, app_state.basic_mode_use_percent);
let style = {

View File

@ -14,24 +14,21 @@ use crate::{
drawing_utils::should_hide_x_label,
Painter,
},
collection::memory::MemHarvest,
collection::memory::MemData,
get_binary_unit_and_denominator,
};
/// Convert memory info into a combined memory label.
#[inline]
fn memory_legend_label(name: &str, data: Option<&MemHarvest>) -> String {
fn memory_legend_label(name: &str, data: Option<&MemData>) -> String {
if let Some(data) = data {
if data.total_bytes > 0 {
let percentage = data.used_bytes as f64 / data.total_bytes as f64 * 100.0;
let (unit, denominator) = get_binary_unit_and_denominator(data.total_bytes);
let used = data.used_bytes as f64 / denominator;
let total = data.total_bytes as f64 / denominator;
let total_bytes = data.total_bytes.get();
let percentage = data.used_bytes as f64 / total_bytes as f64 * 100.0;
let (unit, denominator) = get_binary_unit_and_denominator(total_bytes);
let used = data.used_bytes as f64 / denominator;
let total = total_bytes as f64 / denominator;
format!("{name}:{percentage:3.0}% {used:.1}{unit}/{total:.1}{unit}")
} else {
format!("{name}: 0% 0.0B/0.0B")
}
format!("{name}:{percentage:3.0}% {used:.1}{unit}/{total:.1}{unit}")
} else {
format!("{name}: 0% 0.0B/0.0B")
}
@ -40,7 +37,7 @@ fn memory_legend_label(name: &str, data: Option<&MemHarvest>) -> String {
/// Get graph data.
#[inline]
fn graph_data<'a>(
out: &mut Vec<GraphData<'a>>, name: &str, last_harvest: Option<&'a MemHarvest>,
out: &mut Vec<GraphData<'a>>, name: &str, last_harvest: Option<&'a MemData>,
time: &'a [Instant], values: &'a Values, style: Style,
) {
if !values.no_elements() {
@ -97,10 +94,11 @@ impl Painter {
let timeseries = &data.timeseries_data;
let time = &timeseries.time;
// TODO: Add a "no data" option here/to time graph if there is no entries
graph_data(
&mut points,
"RAM",
Some(&data.ram_harvest),
data.ram_harvest.as_ref(),
time,
&timeseries.ram,
self.styles.ram_style,

View File

@ -36,10 +36,10 @@ pub struct Data {
pub collection_time: Instant,
pub cpu: Option<cpu::CpuHarvest>,
pub load_avg: Option<cpu::LoadAvgHarvest>,
pub memory: Option<memory::MemHarvest>,
pub memory: Option<memory::MemData>,
#[cfg(not(target_os = "windows"))]
pub cache: Option<memory::MemHarvest>,
pub swap: Option<memory::MemHarvest>,
pub cache: Option<memory::MemData>,
pub swap: Option<memory::MemData>,
pub temperature_sensors: Option<Vec<temperature::TempSensorData>>,
pub network: Option<network::NetworkHarvest>,
pub list_of_processes: Option<Vec<processes::ProcessHarvest>>,
@ -48,9 +48,9 @@ pub struct Data {
#[cfg(feature = "battery")]
pub list_of_batteries: Option<Vec<batteries::BatteryData>>,
#[cfg(feature = "zfs")]
pub arc: Option<memory::MemHarvest>,
pub arc: Option<memory::MemData>,
#[cfg(feature = "gpu")]
pub gpu: Option<Vec<(String, memory::MemHarvest)>>,
pub gpu: Option<Vec<(String, memory::MemData)>>,
}
impl Default for Data {
@ -345,7 +345,7 @@ impl DataCollector {
#[inline]
fn update_gpus(&mut self) {
if self.widgets_to_harvest.use_gpu {
let mut local_gpu: Vec<(String, memory::MemHarvest)> = Vec::new();
let mut local_gpu: Vec<(String, memory::MemData)> = Vec::new();
let mut local_gpu_pids: Vec<HashMap<u32, (u64, u32)>> = Vec::new();
let mut local_gpu_total_mem: u64 = 0;
@ -501,7 +501,7 @@ impl DataCollector {
#[inline]
fn total_memory(&self) -> u64 {
if let Some(memory) = &self.data.memory {
memory.total_bytes
memory.total_bytes.get()
} else {
self.sys.system.total_memory()
}

View File

@ -2,19 +2,20 @@ mod amdgpu_marketing;
use crate::{
app::{filter::Filter, layout_manager::UsedWidgets},
collection::{memory::MemHarvest, temperature::TempSensorData},
collection::{memory::MemData, temperature::TempSensorData},
};
use hashbrown::{HashMap, HashSet};
use std::{
fs,
fs::read_to_string,
fs::{self, read_to_string},
num::NonZeroU64,
path::{Path, PathBuf},
sync::{LazyLock, Mutex},
time::{Duration, Instant},
};
// TODO: May be able to clean up some of these, Option<Vec> for example is a bit redundant.
pub struct AMDGPUData {
pub memory: Option<Vec<(String, MemHarvest)>>,
pub memory: Option<Vec<(String, MemData)>>,
pub temperature: Option<Vec<TempSensorData>>,
pub procs: Option<(u64, Vec<HashMap<u32, (u64, u32)>>)>,
}
@ -415,13 +416,15 @@ pub fn get_amd_vecs(
if let Some(mem) = get_amd_vram(&device_path) {
if widgets_to_harvest.use_mem {
mem_vec.push((
device_name.clone(),
MemHarvest {
total_bytes: mem.total,
used_bytes: mem.used,
},
));
if let Some(total_bytes) = NonZeroU64::new(mem.total) {
mem_vec.push((
device_name.clone(),
MemData {
total_bytes,
used_bytes: mem.used,
},
));
}
}
total_mem += mem.total

View File

@ -1,10 +1,13 @@
//! Memory data collection.
use std::num::NonZeroU64;
#[cfg(not(target_os = "windows"))]
pub(crate) use self::sysinfo::get_cache_usage;
pub(crate) use self::sysinfo::{get_ram_usage, get_swap_usage};
pub mod sysinfo;
// cfg_if::cfg_if! {
// if #[cfg(target_os = "windows")] {
// mod windows;
@ -15,36 +18,19 @@ pub mod sysinfo;
#[cfg(feature = "zfs")]
pub mod arc;
#[derive(Debug, Clone, Default)]
pub struct MemHarvest {
#[derive(Debug, Clone)]
pub struct MemData {
pub used_bytes: u64,
pub total_bytes: u64,
pub total_bytes: NonZeroU64,
}
impl MemHarvest {
/// Return the use percentage. If the total bytes is 0, then this returns `None`.
impl MemData {
/// Return the use percentage.
#[inline]
pub fn checked_percent(&self) -> Option<f64> {
pub fn percentage(&self) -> f64 {
let used = self.used_bytes as f64;
let total = self.total_bytes as f64;
let total = self.total_bytes.get() as f64;
if total == 0.0 {
None
} else {
Some(used / total * 100.0)
}
}
/// Return the use percentage. If the total bytes is 0, then this returns 0.0.
#[inline]
pub fn saturating_percentage(&self) -> f64 {
let used = self.used_bytes as f64;
let total = self.total_bytes as f64;
if total == 0.0 {
0.0
} else {
used / total * 100.0
}
used / total * 100.0
}
}

View File

@ -1,8 +1,10 @@
use super::MemHarvest;
use super::MemData;
/// Return ARC usage.
#[cfg(feature = "zfs")]
pub(crate) fn get_arc_usage() -> Option<MemHarvest> {
pub(crate) fn get_arc_usage() -> Option<MemData> {
use std::num::NonZeroU64;
let (mem_total, mem_used) = {
cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] {
@ -63,12 +65,8 @@ pub(crate) fn get_arc_usage() -> Option<MemHarvest> {
}
};
if mem_total > 0 {
Some(MemHarvest {
total_bytes: mem_total,
used_bytes: mem_used,
})
} else {
None
}
NonZeroU64::new(mem_total).map(|total_bytes| MemData {
total_bytes,
used_bytes: mem_used,
})
}

View File

@ -1,32 +1,27 @@
//! Collecting memory data using sysinfo.
use std::num::NonZeroU64;
use sysinfo::System;
use crate::collection::memory::MemHarvest;
use crate::collection::memory::MemData;
/// Returns RAM usage.
pub(crate) fn get_ram_usage(sys: &System) -> Option<MemHarvest> {
let mem_used = sys.used_memory();
let mem_total = sys.total_memory();
Some(MemHarvest {
used_bytes: mem_used,
total_bytes: mem_total,
#[inline]
fn get_usage(used: u64, total: u64) -> Option<MemData> {
NonZeroU64::new(total).map(|total_bytes| MemData {
total_bytes,
used_bytes: used,
})
}
/// Returns RAM usage.
pub(crate) fn get_ram_usage(sys: &System) -> Option<MemData> {
get_usage(sys.used_memory(), sys.total_memory())
}
/// Returns SWAP usage.
pub(crate) fn get_swap_usage(sys: &System) -> Option<MemHarvest> {
let mem_used = sys.used_swap();
let mem_total = sys.total_swap();
if mem_total > 0 {
Some(MemHarvest {
used_bytes: mem_used,
total_bytes: mem_total,
})
} else {
None
}
pub(crate) fn get_swap_usage(sys: &System) -> Option<MemData> {
get_usage(sys.used_swap(), sys.total_swap())
}
/// Returns cache usage. sysinfo has no way to do this directly but it should
@ -37,12 +32,9 @@ pub(crate) fn get_swap_usage(sys: &System) -> Option<MemHarvest> {
/// Windows, this will always be 0. For more information, see [docs](https://docs.rs/sysinfo/latest/sysinfo/struct.System.html#method.available_memory)
/// and [memory explanation](https://askubuntu.com/questions/867068/what-is-available-memory-while-using-free-command)
#[cfg(not(target_os = "windows"))]
pub(crate) fn get_cache_usage(sys: &System) -> Option<MemHarvest> {
pub(crate) fn get_cache_usage(sys: &System) -> Option<MemData> {
let mem_used = sys.available_memory().saturating_sub(sys.free_memory());
let mem_total = sys.total_memory();
Some(MemHarvest {
total_bytes: mem_total,
used_bytes: mem_used,
})
get_usage(mem_used, mem_total)
}

View File

@ -1,4 +1,4 @@
use std::sync::OnceLock;
use std::{num::NonZeroU64, sync::OnceLock};
use hashbrown::HashMap;
use nvml_wrapper::{
@ -7,13 +7,13 @@ use nvml_wrapper::{
use crate::{
app::{filter::Filter, layout_manager::UsedWidgets},
collection::{memory::MemHarvest, temperature::TempSensorData},
collection::{memory::MemData, temperature::TempSensorData},
};
pub static NVML_DATA: OnceLock<Result<Nvml, NvmlError>> = OnceLock::new();
pub struct GpusData {
pub memory: Option<Vec<(String, MemHarvest)>>,
pub memory: Option<Vec<(String, MemData)>>,
pub temperature: Option<Vec<TempSensorData>>,
pub procs: Option<(u64, Vec<HashMap<u32, (u64, u32)>>)>,
}
@ -58,13 +58,15 @@ pub fn get_nvidia_vecs(
if let Ok(name) = device.name() {
if widgets_to_harvest.use_mem {
if let Ok(mem) = device.memory_info() {
mem_vec.push((
name.clone(),
MemHarvest {
total_bytes: mem.total,
used_bytes: mem.used,
},
));
if let Some(total_bytes) = NonZeroU64::new(mem.total) {
mem_vec.push((
name.clone(),
MemData {
total_bytes,
used_bytes: mem.used,
},
));
}
}
}