diff --git a/README.md b/README.md index ec119aa..ce8a7bb 100644 --- a/README.md +++ b/README.md @@ -604,7 +604,7 @@ You need [`cargo`](https://www.rust-lang.org/learn/get-started), Rust's build to The `--release` profile in Gupax is set to prefer code performance & small binary sizes over compilation speed (see [`Cargo.toml`](https://github.com/hinto-janaiyo/gupax/blob/main/Cargo.toml)). Gupax itself (with all dependencies already built) takes around 1m30s to build (vs 10s on a normal `--release`) with a Ryzen 5950x. -There are `38` unit tests throughout the codebase files, you should probably run: +There are `40` unit tests throughout the codebase files, you should probably run: ``` cargo test ``` diff --git a/src/README.md b/src/README.md index e8e2d8f..7da5781 100644 --- a/src/README.md +++ b/src/README.md @@ -18,7 +18,7 @@ ## Structure | File/Folder | Purpose | |--------------|---------| -| benchmark.rs | Code for handling [XMRig's benchmark data](https://xmrig.com/docs/api/1/benchmark) +| cpu.json | [XMRig benchmark data in JSON](https://github.com/hinto-janaiyo/xmrig-benchmarks) | constants.rs | General constants used in Gupax | disk.rs | Code for writing to disk: `state.toml/node.toml/pool.toml`; This holds the structs for the [State] struct | ferris.rs | Cute crab bytes diff --git a/src/benchmark.rs b/src/benchmark.rs deleted file mode 100644 index 88e73e2..0000000 --- a/src/benchmark.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Gupax - GUI Uniting P2Pool And XMRig -// -// Copyright (c) 2022-2023 hinto-janai -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -// This file contains backend code for handling XMRig's benchmark data: -// - HTTP(s) fetchs to [https://xmrig.com] -// - (De)serialization of JSON data -// - (De)serialization of CPU topology XML (./xmrig --export topology) - -use serde::{Serialize, Deserialize}; -use std::fmt::Write; - -// Input: Full [&str] of XMRig's [topology.xml] file -// Output: The CPU name formatted so it's usable as the endpoint, e.g: [AMD+Ryzen+9+5950X+16-Core+Processor] -fn cpu_name_from_xml(xml: &str) -> Option { - // A minimal matching struct for the [CPUModel] field in the XML file - #[derive(Debug, Serialize, Deserialize, PartialEq)] - struct Info { // - name: String, // name="CPUModel" - value: String, // value="Ryzen ..." - } - - // Regex to find matching field - let regex = regex::Regex::new("\"CPUModel\"").unwrap(); - - for line in xml.lines() { - if !regex.is_match(&line) { continue } - - // If found, attempt to serialize XML proper - if let Ok(info) = serde_xml_rs::from_str::(&line) { - // Return early if empty - if info.value.is_empty() { - return None - } - // If serialized, turn whitespaces into '+' - let words: Vec<&str> = info.value.split_whitespace().collect(); - let last_word = words.len(); - let mut result = String::new(); - let mut n = 1; - for word in words.iter() { - match n == last_word { - false => write!(result, "{}+", word), - true => write!(result, "{}", word), - }; - n += 1; - } - return Some(result) - } - } - - // If loop didn't return early, return none - None -} - -//---------------------------------------------------------------------------------------------------- TESTS -#[cfg(test)] -mod test { - #[test] - fn get_cpu_from_xml() { - let string = -r#" - - - - - - - - - -"#; - assert_eq!(crate::benchmark::cpu_name_from_xml(&string).unwrap(), "AMD+Ryzen+9+5950X+16-Core+Processor"); - } -} diff --git a/src/constants.rs b/src/constants.rs index b91db31..4b525bf 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -174,6 +174,7 @@ pub const STATUS_XMRIG_THREADS: &str = "The amount of threads XMRig is curre // Status Submenus pub const STATUS_SUBMENU_PROCESSES: &str = "View the status of process related data for [Gupax|P2Pool|XMRig]"; pub const STATUS_SUBMENU_P2POOL: &str = "View P2Pool specific data"; +pub const STATUS_SUBMENU_HASHRATE: &str = "Compare your CPU hashrate with others"; //-- P2Pool pub const STATUS_SUBMENU_PAYOUT: &str = "The total amount of payouts received via P2Pool across all time. This includes all payouts you have ever received using Gupax and P2Pool."; pub const STATUS_SUBMENU_XMR: &str = "The total of XMR mined via P2Pool across all time. This includes all the XMR you have ever mined using Gupax and P2Pool."; @@ -200,6 +201,21 @@ pub const STATUS_SUBMENU_P2POOL_DOMINANCE: &str = "The percent of hashrate pub const STATUS_SUBMENU_YOUR_P2POOL_DOMINANCE: &str = "The percent of hashrate you account for in P2Pool"; pub const STATUS_SUBMENU_YOUR_MONERO_DOMINANCE: &str = "The percent of hashrate you account for in the entire Monero network"; pub const STATUS_SUBMENU_PROGRESS_BAR: &str = "The next time Gupax will update P2Pool stats. Each [*] is 900ms (updates roughly every 54 seconds)"; +//-- Benchmarks +pub const STATUS_SUBMENU_YOUR_CPU: &str = "The CPU detected by Gupax"; +pub const STATUS_SUBMENU_YOUR_BENCHMARKS: &str = "How many benchmarks your CPU has had uploaded to [https://xmrig.com/benchmark] "; +pub const STATUS_SUBMENU_YOUR_RANK: &str = "Your CPU's rank out of all CPUs listed on [https://xmrig.com/benchmark] (higher is better)"; +pub const STATUS_SUBMENU_YOUR_HIGH: &str = "The highest hashrate recorded for your CPU on [https://xmrig.com/benchmark]"; +pub const STATUS_SUBMENU_YOUR_AVERAGE: &str = "The average hashrate of your CPU based off the data at [https://xmrig.com/benchmark]"; +pub const STATUS_SUBMENU_YOUR_LOW: &str = "The lowest hashrate recorded for your CPU on [https://xmrig.com/benchmark]"; +pub const STATUS_SUBMENU_OTHER_CPUS: &str = "A list of ALL the recorded CPU benchmarks. The CPUs most similar to yours are listed first. All this data is taken from [https://github.com/hinto-janai/xmrig-benchmarks] which itself takes from [https://xmrig.com/benchmark]."; +pub const STATUS_SUBMENU_OTHER_CPU: &str = "The CPU name"; +pub const STATUS_SUBMENU_OTHER_RELATIVE: &str = "The relative hashrate power compared to the fastest recorded CPU, which is current: [AMD EPYC 7T83 64-Core Processor]"; +pub const STATUS_SUBMENU_OTHER_HIGH: &str = "Highest hashrate record"; +pub const STATUS_SUBMENU_OTHER_AVERAGE: &str = "Average hashrate"; +pub const STATUS_SUBMENU_OTHER_LOW: &str = "Lowest hashrate record"; +pub const STATUS_SUBMENU_OTHER_RANK: &str = "The rank of this CPU out of [1567] (lower is better)"; +pub const STATUS_SUBMENU_OTHER_BENCHMARKS: &str = "How many benchmarks this CPU has had posted to [https://xmrig.com/benchmark]"; // Gupax pub const GUPAX_UPDATE: &str = "Check for updates on Gupax, P2Pool, and XMRig via GitHub's API and upgrade automatically"; diff --git a/src/disk.rs b/src/disk.rs index 9069b88..f741e3a 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -825,6 +825,7 @@ pub enum File { pub enum Submenu { Processes, P2pool, + Benchmarks, } impl Default for Submenu { diff --git a/src/gupax.rs b/src/gupax.rs index 3a1839e..c243e41 100644 --- a/src/gupax.rs +++ b/src/gupax.rs @@ -82,6 +82,7 @@ pub enum Ratio { //---------------------------------------------------------------------------------------------------- Gupax impl crate::disk::Gupax { + #[inline(always)] pub fn show(&mut self, og: &Arc>, state_path: &Path, update: &Arc>, file_window: &Arc>, error_state: &mut ErrorState, restart: &Arc>, width: f32, height: f32, frame: &mut eframe::Frame, _ctx: &egui::Context, ui: &mut egui::Ui) { // Update button + Progress bar debug!("Gupax Tab | Rendering [Update] button + progress bar"); diff --git a/src/helper.rs b/src/helper.rs index c61bcfd..33f12d6 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -1731,6 +1731,8 @@ pub struct PubXmrigApi { pub diff: HumanNumber, pub accepted: HumanNumber, pub rejected: HumanNumber, + + pub hashrate_raw: f32, } impl Default for PubXmrigApi { @@ -1750,6 +1752,7 @@ impl PubXmrigApi { diff: HumanNumber::unknown(), accepted: HumanNumber::unknown(), rejected: HumanNumber::unknown(), + hashrate_raw: 0.0, } } @@ -1780,6 +1783,11 @@ impl PubXmrigApi { // Formats raw private data into ready-to-print human readable version. fn update_from_priv(public: &Arc>, private: PrivXmrigApi) { let mut public = lock!(public); + let hashrate_raw = match private.hashrate.total.get(0) { + Some(Some(h)) => *h, + _ => 0.0, + }; + *public = Self { worker_id: private.worker_id, resources: HumanNumber::from_load(private.resources.load_average), @@ -1787,6 +1795,7 @@ impl PubXmrigApi { diff: HumanNumber::from_u128(private.connection.diff), accepted: HumanNumber::from_u128(private.connection.accepted), rejected: HumanNumber::from_u128(private.connection.rejected), + hashrate_raw, ..std::mem::take(&mut *public) } } diff --git a/src/human.rs b/src/human.rs index be9586d..b13e9c9 100644 --- a/src/human.rs +++ b/src/human.rs @@ -116,6 +116,9 @@ impl HumanNumber { pub fn from_str(s: &str) -> Self { Self(s.to_string()) } + pub fn to_hashrate(f: f32) -> Self { + Self(format!("{} H/s", Self::from_f32(f))) + } pub fn to_percent(f: f32) -> Self { if f < 0.01 { Self("0%".to_string()) diff --git a/src/main.rs b/src/main.rs index aeaee2d..bdfbd6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,6 +51,7 @@ use std::{ }; // Sysinfo use sysinfo::SystemExt; +use sysinfo::CpuExt; // Modules //mod benchmark; mod ferris; @@ -140,6 +141,7 @@ pub struct App { // actual stats, and all the functions needed to mutate them. gupax_p2pool_api: Arc>, // Static stuff + benchmarks: Vec, // XMRig CPU benchmarks pid: sysinfo::Pid, // Gupax's PID max_threads: usize, // Max amount of detected system threads now: Instant, // Internal timer @@ -199,6 +201,18 @@ impl App { }; let pub_sys = arc_mut!(Sys::new()); + // CPU Benchmark data initialization. + info!("App Init | Initializing CPU benchmarks..."); + let benchmarks: Vec = { + let cpu = sysinfo.cpus()[0].brand(); + let mut json: Vec = serde_json::from_slice(include_bytes!("cpu.json")).unwrap(); + json.sort_by(|a, b| { + cmp_f64(strsim::jaro(&b.cpu, &cpu), strsim::jaro(&a.cpu, &cpu)) + }); + json + }; + info!("App Init | Assuming user's CPU is: {}", benchmarks[0].cpu); + info!("App Init | The rest of the [App]..."); let mut app = Self { tab: Tab::default(), @@ -232,6 +246,7 @@ impl App { no_startup: false, gupax_p2pool_api: arc_mut!(GupaxP2poolApi::new()), pub_sys, + benchmarks, pid, max_threads: num_cpus::get(), now, @@ -507,6 +522,18 @@ impl Default for Tab { } } +//---------------------------------------------------------------------------------------------------- CPU Benchmarks. +#[derive(Debug,Serialize,Deserialize)] +pub struct Benchmark { + pub cpu: String, + pub rank: u16, + pub percent: f32, + pub benchmarks: u16, + pub average: f32, + pub high: f32, + pub low: f32, +} + //---------------------------------------------------------------------------------------------------- [Restart] Enum #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Restart { @@ -688,6 +715,7 @@ impl KeyPressed { } //---------------------------------------------------------------------------------------------------- Init functions +#[inline(always)] fn init_text_styles(ctx: &egui::Context, width: f32) { let scale = width / 30.0; let mut style = (*ctx.style()).clone(); @@ -711,6 +739,7 @@ fn init_text_styles(ctx: &egui::Context, width: f32) { ctx.request_repaint(); } +#[inline(always)] fn init_logger(now: Instant) { use env_logger::fmt::Color; let filter_env = std::env::var("RUST_LOG").unwrap_or_else(|_| "INFO".to_string()); @@ -746,6 +775,7 @@ fn init_logger(now: Instant) { info!("Log level ... {}", filter); } +#[inline(always)] fn init_options(initial_window_size: Option) -> NativeOptions { let mut options = eframe::NativeOptions::default(); options.min_window_size = Some(Vec2::new(APP_MIN_WIDTH, APP_MIN_HEIGHT)); @@ -764,6 +794,7 @@ fn init_options(initial_window_size: Option) -> NativeOptions { options } +#[inline(always)] fn init_auto(app: &mut App) { // Return early if [--no-startup] was not passed if app.no_startup { @@ -831,6 +862,7 @@ fn reset_state(path: &PathBuf) -> Result<(), TomlError> { } } +#[inline(always)] fn reset_nodes(path: &PathBuf) -> Result<(), TomlError> { match Node::create_new(path) { Ok(_) => { info!("Resetting [node.toml] ... OK"); Ok(()) }, @@ -838,6 +870,7 @@ fn reset_nodes(path: &PathBuf) -> Result<(), TomlError> { } } +#[inline(always)] fn reset_pools(path: &PathBuf) -> Result<(), TomlError> { match Pool::create_new(path) { Ok(_) => { info!("Resetting [pool.toml] ... OK"); Ok(()) }, @@ -845,6 +878,7 @@ fn reset_pools(path: &PathBuf) -> Result<(), TomlError> { } } +#[inline(always)] fn reset_gupax_p2pool_api(path: &PathBuf) -> Result<(), TomlError> { match GupaxP2poolApi::create_new(path) { Ok(_) => { info!("Resetting GupaxP2poolApi ... OK"); Ok(()) }, @@ -852,6 +886,7 @@ fn reset_gupax_p2pool_api(path: &PathBuf) -> Result<(), TomlError> { } } +#[inline(always)] fn reset(path: &PathBuf, state: &PathBuf, node: &PathBuf, pool: &PathBuf, gupax_p2pool_api: &PathBuf) { let mut code = 0; // Attempt to remove directory first @@ -888,6 +923,7 @@ fn reset(path: &PathBuf, state: &PathBuf, node: &PathBuf, pool: &PathBuf, gupax_ } //---------------------------------------------------------------------------------------------------- Misc functions +#[inline(always)] fn parse_args>(mut app: App, panic: S) -> App { info!("Parsing CLI arguments..."); let mut args: Vec = env::args().collect(); @@ -930,6 +966,7 @@ fn parse_args>(mut app: App, panic: S) -> App { } // Get absolute [Gupax] binary path +#[inline(always)] pub fn get_exe() -> Result { match std::env::current_exe() { Ok(path) => { Ok(path.display().to_string()) }, @@ -938,6 +975,7 @@ pub fn get_exe() -> Result { } // Get absolute [Gupax] directory path +#[inline(always)] pub fn get_exe_dir() -> Result { match std::env::current_exe() { Ok(mut path) => { path.pop(); Ok(path.display().to_string()) }, @@ -947,6 +985,7 @@ pub fn get_exe_dir() -> Result { // Clean any [gupax_update_.*] directories // The trailing random bits must be exactly 10 alphanumeric characters +#[inline(always)] pub fn clean_dir() -> Result<(), anyhow::Error> { let regex = Regex::new("^gupax_update_[A-Za-z0-9]{10}$").unwrap(); for entry in std::fs::read_dir(get_exe_dir()?)? { @@ -964,6 +1003,7 @@ pub fn clean_dir() -> Result<(), anyhow::Error> { } // Print disk files to console +#[inline(always)] fn print_disk_file(path: &PathBuf) { match std::fs::read_to_string(path) { Ok(string) => { print!("{}", string); exit(0); }, @@ -972,6 +1012,7 @@ fn print_disk_file(path: &PathBuf) { } // Prints the GupaxP2PoolApi files. +#[inline(always)] fn print_gupax_p2pool_api(gupax_p2pool_api: &Arc>) { let api = lock!(gupax_p2pool_api); let log = match std::fs::read_to_string(&api.path_log) { @@ -994,6 +1035,16 @@ fn print_gupax_p2pool_api(gupax_p2pool_api: &Arc>) { exit(0); } +#[inline(always)] +fn cmp_f64(a: f64, b: f64) -> std::cmp::Ordering { + match (a <= b, a >= b) { + (false, true) => std::cmp::Ordering::Greater, + (true, false) => std::cmp::Ordering::Less, + (true, true) => std::cmp::Ordering::Equal, + _ => std::cmp::Ordering::Less, + } +} + //---------------------------------------------------------------------------------------------------- Main [App] frame fn main() { let now = Instant::now(); @@ -1018,6 +1069,7 @@ fn main() { } impl eframe::App for App { + #[inline(always)] fn on_close_event(&mut self) -> bool { if self.state.gupax.ask_before_quit { // If we're already on the [ask_before_quit] screen and @@ -1037,6 +1089,7 @@ impl eframe::App for App { } } + #[inline(always)] fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { // *-------* // | DEBUG | @@ -1104,8 +1157,9 @@ impl eframe::App for App { match self.tab { Tab::Status => { match self.state.status.submenu { - Submenu::Processes => self.state.status.submenu = Submenu::P2pool, + Submenu::Processes => self.state.status.submenu = Submenu::Benchmarks, Submenu::P2pool => self.state.status.submenu = Submenu::Processes, + Submenu::Benchmarks => self.state.status.submenu = Submenu::P2pool, } }, Tab::Gupax => flip!(self.state.gupax.simple), @@ -1119,7 +1173,8 @@ impl eframe::App for App { Tab::Status => { match self.state.status.submenu { Submenu::Processes => self.state.status.submenu = Submenu::P2pool, - Submenu::P2pool => self.state.status.submenu = Submenu::Processes, + Submenu::P2pool => self.state.status.submenu = Submenu::Benchmarks, + Submenu::Benchmarks => self.state.status.submenu = Submenu::Processes, } }, Tab::Gupax => flip!(self.state.gupax.simple), @@ -1517,7 +1572,11 @@ impl eframe::App for App { match self.tab { Tab::Status => { ui.group(|ui| { - let width = (ui.available_width() / 2.0)-10.5; + let width = (ui.available_width() / 3.0)-14.0; + if ui.add_sized([width, height], SelectableLabel::new(self.state.status.submenu == Submenu::Benchmarks, "Benchmarks")).on_hover_text(STATUS_SUBMENU_HASHRATE).clicked() { + self.state.status.submenu = Submenu::Benchmarks; + } + ui.separator(); if ui.add_sized([width, height], SelectableLabel::new(self.state.status.submenu == Submenu::P2pool, "P2Pool")).on_hover_text(STATUS_SUBMENU_P2POOL).clicked() { self.state.status.submenu = Submenu::P2pool; } @@ -1800,7 +1859,7 @@ path_xmr: {:#?}\n } Tab::Status => { debug!("App | Entering [Status] Tab"); - crate::disk::Status::show(&mut self.state.status, &self.pub_sys, &self.p2pool_api, &self.xmrig_api, &self.p2pool_img, &self.xmrig_img, p2pool_is_alive, xmrig_is_alive, self.max_threads, &self.gupax_p2pool_api, self.width, self.height, ctx, ui); + crate::disk::Status::show(&mut self.state.status, &self.pub_sys, &self.p2pool_api, &self.xmrig_api, &self.p2pool_img, &self.xmrig_img, p2pool_is_alive, xmrig_is_alive, self.max_threads, &self.gupax_p2pool_api, &self.benchmarks, self.width, self.height, ctx, ui); } Tab::Gupax => { debug!("App | Entering [Gupax] Tab"); @@ -1840,4 +1899,21 @@ mod test { assert!(!Regex::is_match(&r.port, "0")); assert!(!Regex::is_match(&r.port, "65536")); } + + #[test] + fn detect_benchmark_cpu() { + use super::{Benchmark,cmp_f64}; + + let cpu = "AMD Ryzen 9 5950X 16-Core Processor"; + + let benchmarks: Vec = { + let mut json: Vec = serde_json::from_slice(include_bytes!("cpu.json")).unwrap(); + json.sort_by(|a, b| { + cmp_f64(strsim::jaro(&b.cpu, &cpu), strsim::jaro(&a.cpu, &cpu)) + }); + json + }; + + assert!(benchmarks[0].cpu == "AMD Ryzen 9 5950X 16-Core Processor"); + } } diff --git a/src/p2pool.rs b/src/p2pool.rs index b269466..f7631c4 100644 --- a/src/p2pool.rs +++ b/src/p2pool.rs @@ -33,6 +33,7 @@ use regex::Regex; use log::*; impl crate::disk::P2pool { + #[inline(always)] pub fn show(&mut self, node_vec: &mut Vec<(String, Node)>, _og: &Arc>, ping: &Arc>, regex: &Regexes, process: &Arc>, api: &Arc>, buffer: &mut String, width: f32, height: f32, _ctx: &egui::Context, ui: &mut egui::Ui) { let text_edit = height / 25.0; //---------------------------------------------------------------------------------------------------- [Simple] Console diff --git a/src/status.rs b/src/status.rs index 087c833..d89fa36 100644 --- a/src/status.rs +++ b/src/status.rs @@ -28,6 +28,7 @@ use crate::{ GupaxP2poolApi, PayoutView, human::HumanNumber, + Benchmark, }; use std::sync::{Arc,Mutex}; use log::*; @@ -36,12 +37,13 @@ use egui::{ TextStyle::Monospace, TextStyle::Name, TextEdit, - SelectableLabel, - Slider, + SelectableLabel,Hyperlink, + Slider,ProgressBar,Spinner, }; impl crate::disk::Status { -pub fn show(&mut self, sys: &Arc>, p2pool_api: &Arc>, xmrig_api: &Arc>, p2pool_img: &Arc>, xmrig_img: &Arc>, p2pool_alive: bool, xmrig_alive: bool, max_threads: usize, gupax_p2pool_api: &Arc>, width: f32, height: f32, _ctx: &egui::Context, ui: &mut egui::Ui) { +#[inline(always)] +pub fn show(&mut self, sys: &Arc>, p2pool_api: &Arc>, xmrig_api: &Arc>, p2pool_img: &Arc>, xmrig_img: &Arc>, p2pool_alive: bool, xmrig_alive: bool, max_threads: usize, gupax_p2pool_api: &Arc>, benchmarks: &[Benchmark], width: f32, height: f32, _ctx: &egui::Context, ui: &mut egui::Ui) { //---------------------------------------------------------------------------------------------------- [Processes] if self.submenu == Submenu::Processes { let width = (width/3.0)-(SPACE*1.666); @@ -268,6 +270,115 @@ pub fn show(&mut self, sys: &Arc>, p2pool_api: &Arc 100.0 { + ui.add_sized([width, double], Label::new(format!("Your CPU's is faster than the highest benchmark! It is [{}] faster @ {}!", human, api.hashrate))); + ui.add_sized([width, text], ProgressBar::new(1.0)); + } else if api.hashrate_raw == 0.0 { + ui.add_sized([width, text], Label::new("Measuring hashrate...")); + ui.add_sized([width, text], Spinner::new().size(text)); + ui.add_sized([width, text], ProgressBar::new(0.0)); + } else { + ui.add_sized([width, double], Label::new(format!("Your CPU's hashrate is [{}] of the highest benchmark @ {}", human, api.hashrate))); + ui.add_sized([width, text], ProgressBar::new(percent / 100.0)); + } + } else { + ui.set_enabled(xmrig_alive); + ui.add_sized([width, double], Label::new("XMRig is offline. Hashrate cannot be determined.")); + ui.add_sized([width, text], ProgressBar::new(0.0)); + } + }); + + // Comparison + ui.group(|ui| { + ui.add_sized([width, text], Hyperlink::from_label_and_url("Other CPUs", "https://github.com/hinto-janai/xmrig-benchmarks")).on_hover_text(STATUS_SUBMENU_OTHER_CPUS); + }); + + egui::ScrollArea::both().max_width(f32::INFINITY).max_height(height).auto_shrink([false; 2]).show_viewport(ui, |ui, _| { + let width = width / 20.0; + let (cpu, bar, high, average, low, rank, bench) = ( + width*10.0, + width*3.0, + width*2.0, + width*2.0, + width*2.0, + width, + width*2.0, + ); + ui.group(|ui| { + ui.horizontal(|ui| { + ui.add_sized([cpu, double], Label::new("CPU")).on_hover_text(STATUS_SUBMENU_OTHER_CPU); + ui.separator(); + ui.add_sized([bar, double], Label::new("Relative")).on_hover_text(STATUS_SUBMENU_OTHER_RELATIVE); + ui.separator(); + ui.add_sized([high, double], Label::new("High")).on_hover_text(STATUS_SUBMENU_OTHER_HIGH); + ui.separator(); + ui.add_sized([average, double], Label::new("Average")).on_hover_text(STATUS_SUBMENU_OTHER_AVERAGE); + ui.separator(); + ui.add_sized([low, double], Label::new("Low")).on_hover_text(STATUS_SUBMENU_OTHER_LOW); + ui.separator(); + ui.add_sized([rank, double], Label::new("Rank")).on_hover_text(STATUS_SUBMENU_OTHER_RANK); + ui.separator(); + ui.add_sized([bench, double], Label::new("Benchmarks")).on_hover_text(STATUS_SUBMENU_OTHER_BENCHMARKS); + }); + }); + + for benchmark in benchmarks[1..].iter() { + ui.group(|ui| { ui.horizontal(|ui| { + ui.add_sized([cpu, text], Label::new(benchmark.cpu.as_str())); + ui.separator(); + ui.add_sized([bar, text], ProgressBar::new(benchmark.percent / 100.0)); + ui.separator(); + ui.add_sized([high, text], Label::new(HumanNumber::to_hashrate(benchmark.high).as_str())); + ui.separator(); + ui.add_sized([average, text], Label::new(HumanNumber::to_hashrate(benchmark.average).as_str())); + ui.separator(); + ui.add_sized([low, text], Label::new(HumanNumber::to_hashrate(benchmark.low).as_str())); + ui.separator(); + ui.add_sized([rank, text], Label::new(HumanNumber::from_u16(benchmark.rank).as_str())); + ui.separator(); + ui.add_sized([bench, text], Label::new(HumanNumber::from_u16(benchmark.benchmarks).as_str())); + })}); + } + }); } } } diff --git a/src/xmrig.rs b/src/xmrig.rs index 317614e..6c9fcdc 100644 --- a/src/xmrig.rs +++ b/src/xmrig.rs @@ -34,6 +34,7 @@ use regex::Regex; use log::*; impl crate::disk::Xmrig { + #[inline(always)] pub fn show(&mut self, pool_vec: &mut Vec<(String, Pool)>, regex: &Regexes, process: &Arc>, api: &Arc>, buffer: &mut String, width: f32, height: f32, _ctx: &egui::Context, ui: &mut egui::Ui) { let text_edit = height / 25.0; //---------------------------------------------------------------------------------------------------- [Simple] Console