diff --git a/Cargo.lock b/Cargo.lock index acbf562..c9d0833 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1282,6 +1282,7 @@ name = "lact" version = "0.7.1" dependencies = [ "anyhow", + "divan", "lact-cli", "lact-daemon", "lact-gui", @@ -1327,7 +1328,6 @@ dependencies = [ "futures", "indexmap", "insta", - "lact-daemon", "lact-schema", "libdrm_amdgpu_sys", "libflate", @@ -1358,6 +1358,7 @@ dependencies = [ "anyhow", "cairo-rs", "chrono", + "divan", "gtk4", "itertools", "lact-client", diff --git a/Cargo.toml b/Cargo.toml index 02f2240..81584a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,3 +45,5 @@ opt-level = 3 [profile.bench] strip = false debug = 1 +lto = "thin" +codegen-units = 256 diff --git a/lact-daemon/Cargo.toml b/lact-daemon/Cargo.toml index 84fb3c2..fd7dad7 100644 --- a/lact-daemon/Cargo.toml +++ b/lact-daemon/Cargo.toml @@ -48,14 +48,8 @@ copes = { git = "https://gitlab.com/corectrl/copes" } libloading = "0.8.6" [dev-dependencies] -divan = { workspace = true } pretty_assertions = { workspace = true } -lact-daemon = { path = ".", features = ["bench"] } insta = { version = "1.41.1", features = ["json", "yaml"] } [build-dependencies] bindgen = "0.68" - -[[bench]] -name = "daemon" -harness = false diff --git a/lact-daemon/benches/daemon.rs b/lact-daemon/benches/daemon.rs deleted file mode 100644 index f90e26e..0000000 --- a/lact-daemon/benches/daemon.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - // Include the daemon lib - let _ = lact_daemon::MODULE_CONF_PATH; - divan::main(); -} diff --git a/lact-gui/Cargo.toml b/lact-gui/Cargo.toml index b746f70..d6e1ec6 100644 --- a/lact-gui/Cargo.toml +++ b/lact-gui/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" default = ["gtk-tests"] gtk-tests = [] adw = ["dep:adw", "relm4/libadwaita"] +bench = ["dep:divan"] [dependencies] lact-client = { path = "../lact-client" } @@ -37,5 +38,7 @@ itertools = "0.13.0" thread-priority = "1.1.0" +divan = { workspace = true, optional = true } + [dev-dependencies] pretty_assertions = "1.4.0" diff --git a/lact-gui/src/app/graphs_window/plot/imp.rs b/lact-gui/src/app/graphs_window/plot/imp.rs index fe5d7ce..04864f7 100644 --- a/lact-gui/src/app/graphs_window/plot/imp.rs +++ b/lact-gui/src/app/graphs_window/plot/imp.rs @@ -105,14 +105,19 @@ impl PlotData { self.push_secondary_line_series_with_time(name, point, chrono::Local::now().naive_local()); } - fn push_line_series_with_time(&mut self, name: &str, point: f64, time: NaiveDateTime) { + pub(super) fn push_line_series_with_time( + &mut self, + name: &str, + point: f64, + time: NaiveDateTime, + ) { self.line_series .entry(name.to_owned()) .or_default() .push((time.and_utc().timestamp_millis(), point)); } - pub fn push_secondary_line_series_with_time( + pub(super) fn push_secondary_line_series_with_time( &mut self, name: &str, point: f64, diff --git a/lact-gui/src/app/graphs_window/plot/render_thread.rs b/lact-gui/src/app/graphs_window/plot/render_thread.rs index fc2dce6..67dd61e 100644 --- a/lact-gui/src/app/graphs_window/plot/render_thread.rs +++ b/lact-gui/src/app/graphs_window/plot/render_thread.rs @@ -94,62 +94,15 @@ impl RenderThread { match current_request.take() { Some(Request::Render(render_request)) => { - // Create a new ImageSurface for Cairo rendering. - let mut surface = ImageSurface::create( - cairo::Format::ARgb32, - (render_request.width * render_request.supersample_factor) as i32, - (render_request.height * render_request.supersample_factor) as i32, - ) - .unwrap(); - - let cairo_context = CairoContext::new(&surface).unwrap(); - - // Don't use Cairo's default antialiasing, it makes the lines look too blurry - // Supersampling is our 2D anti-aliasing solution. - if render_request.supersample_factor > 1 { - cairo_context.set_antialias(cairo::Antialias::None); - } - - let cairo_backend = CairoBackend::new( - &cairo_context, - // Supersample the rendering - ( - render_request.width * render_request.supersample_factor, - render_request.height * render_request.supersample_factor, - ), - ) - .unwrap(); - - if let Err(err) = render_request.draw(cairo_backend) { - error!("Failed to plot chart: {err:?}") - } - - match ( - surface.to_texture(), - last_texture.lock().unwrap().deref_mut(), - ) { - // Successfully generated a new texture, but the old texture is also there - (Some(texture), Some(last_texture)) => { - *last_texture = texture; - } - // If texture conversion failed, keep the old texture if it's present. - (None, None) => { - error!("Failed to convert cairo surface to gdk texture, not overwriting old one"); - } - // Update the last texture, if The old texture wasn't ever generated (None), - // No matter the result of conversion - (result, last_texture) => { - *last_texture = result; - } - }; - } + process_request(render_request, last_texture); + } // Terminate the thread if a Terminate request is received. Some(Request::Terminate) => break, None => {} } } - }) - .unwrap(); + }) + .unwrap(); Self { state, @@ -177,6 +130,57 @@ impl RenderThread { } } +fn process_request(render_request: RenderRequest, last_texture: &Mutex>) { + // Create a new ImageSurface for Cairo rendering. + let mut surface = ImageSurface::create( + cairo::Format::ARgb32, + (render_request.width * render_request.supersample_factor) as i32, + (render_request.height * render_request.supersample_factor) as i32, + ) + .unwrap(); + + let cairo_context = CairoContext::new(&surface).unwrap(); + + // Don't use Cairo's default antialiasing, it makes the lines look too blurry + // Supersampling is our 2D anti-aliasing solution. + if render_request.supersample_factor > 1 { + cairo_context.set_antialias(cairo::Antialias::None); + } + + let cairo_backend = CairoBackend::new( + &cairo_context, + // Supersample the rendering + ( + render_request.width * render_request.supersample_factor, + render_request.height * render_request.supersample_factor, + ), + ) + .unwrap(); + + if let Err(err) = render_request.draw(cairo_backend) { + error!("Failed to plot chart: {err:?}") + } + + match ( + surface.to_texture(), + last_texture.lock().unwrap().deref_mut(), + ) { + // Successfully generated a new texture, but the old texture is also there + (Some(texture), Some(last_texture)) => { + *last_texture = texture; + } + // If texture conversion failed, keep the old texture if it's present. + (None, None) => { + error!("Failed to convert cairo surface to gdk texture, not overwriting old one"); + } + // Update the last texture, if The old texture wasn't ever generated (None), + // No matter the result of conversion + (result, last_texture) => { + *last_texture = result; + } + }; +} + // Implement the default constructor for RenderThread using the `new` method. impl Default for RenderThread { fn default() -> Self { @@ -389,3 +393,56 @@ impl RenderRequest { Ok(()) } } + +#[cfg(feature = "bench")] +mod benches { + use super::{process_request, RenderRequest}; + use crate::app::graphs_window::plot::PlotData; + use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; + use divan::{counter::ItemsCount, Bencher}; + use std::sync::Mutex; + + #[divan::bench] + fn render_plot(bencher: Bencher) { + let last_texture = &Mutex::new(None); + + bencher + .with_inputs(sample_plot_data) + .input_counter(|_| ItemsCount::new(1usize)) + .bench_values(|data| { + let request = RenderRequest { + title: "bench render".into(), + value_suffix: "%".into(), + secondary_value_suffix: "".into(), + y_label_area_relative_size: 1.0, + secondary_y_label_relative_area_size: 1.0, + data, + width: 1920, + height: 1080, + supersample_factor: 4, + time_period_seconds: 60, + }; + + process_request(request, last_texture) + }); + } + + fn sample_plot_data() -> PlotData { + let mut data = PlotData::default(); + + // Simulate 1 minute plot with 4 values per second + for sec in 0..60 { + for milli in [0, 250, 500, 750] { + let datetime = NaiveDateTime::new( + NaiveDate::from_ymd_opt(2025, 1, 1).unwrap(), + NaiveTime::from_hms_milli_opt(0, 0, sec, milli).unwrap(), + ); + + data.push_line_series_with_time("GPU", 100.0, datetime); + data.push_secondary_line_series_with_time("GPU Secondary", 10.0, datetime); + } + } + + data + } +} diff --git a/lact/Cargo.toml b/lact/Cargo.toml index 7d114cc..5404dd6 100644 --- a/lact/Cargo.toml +++ b/lact/Cargo.toml @@ -13,3 +13,12 @@ lact-schema = { path = "../lact-schema", features = ["args"] } lact-cli = { path = "../lact-cli" } lact-gui = { path = "../lact-gui", optional = true } anyhow = { workspace = true } + +[dev-dependencies] +divan = { workspace = true } +lact-daemon = { path = "../lact-daemon", features = ["bench"] } +lact-gui = { path = "../lact-gui", features = ["bench"] } + +[[bench]] +name = "bench" +harness = false diff --git a/lact/benches/bench.rs b/lact/benches/bench.rs new file mode 100644 index 0000000..ac09dbe --- /dev/null +++ b/lact/benches/bench.rs @@ -0,0 +1,7 @@ +fn main() { + // Include crates in the binary + let _ = lact_daemon::run; + let _ = lact_gui::run; + + divan::main(); +}