dev: add benchmark for drawing plots

This commit is contained in:
Ilya Zlobintsev 2025-02-08 20:54:46 +02:00
parent ff55dc7634
commit 0894f9ae9d
9 changed files with 138 additions and 65 deletions

3
Cargo.lock generated
View File

@ -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",

View File

@ -45,3 +45,5 @@ opt-level = 3
[profile.bench]
strip = false
debug = 1
lto = "thin"
codegen-units = 256

View File

@ -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

View File

@ -1,5 +0,0 @@
fn main() {
// Include the daemon lib
let _ = lact_daemon::MODULE_CONF_PATH;
divan::main();
}

View File

@ -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"

View File

@ -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,

View File

@ -94,6 +94,43 @@ impl RenderThread {
match current_request.take() {
Some(Request::Render(render_request)) => {
process_request(render_request, last_texture);
}
// Terminate the thread if a Terminate request is received.
Some(Request::Terminate) => break,
None => {}
}
}
})
.unwrap();
Self {
state,
thread_handle: Some(thread_handle),
}
}
/// Replace the current render request with a new one (effectively dropping possible pending frame)
/// Returns dropped request if any
pub fn replace_render_request(&self, request: RenderRequest) -> Option<RenderRequest> {
let mut current_request = self.state.current_request.lock().unwrap();
let result = current_request.replace(Request::Render(request));
self.state.request_condition_variable.notify_one(); // Notify the thread to start rendering.
match result? {
Request::Render(render) => Some(render),
Request::Terminate => None,
}
}
/// Return the last texture.
/// Requests that weren't processed in time or resulted in error are dropped.
pub fn get_last_texture(&self) -> Option<MemoryTexture> {
self.state.last_texture.lock().unwrap().deref().clone()
}
}
fn process_request(render_request: RenderRequest, last_texture: &Mutex<Option<MemoryTexture>>) {
// Create a new ImageSurface for Cairo rendering.
let mut surface = ImageSurface::create(
cairo::Format::ARgb32,
@ -143,39 +180,6 @@ impl RenderThread {
}
};
}
// Terminate the thread if a Terminate request is received.
Some(Request::Terminate) => break,
None => {}
}
}
})
.unwrap();
Self {
state,
thread_handle: Some(thread_handle),
}
}
/// Replace the current render request with a new one (effectively dropping possible pending frame)
/// Returns dropped request if any
pub fn replace_render_request(&self, request: RenderRequest) -> Option<RenderRequest> {
let mut current_request = self.state.current_request.lock().unwrap();
let result = current_request.replace(Request::Render(request));
self.state.request_condition_variable.notify_one(); // Notify the thread to start rendering.
match result? {
Request::Render(render) => Some(render),
Request::Terminate => None,
}
}
/// Return the last texture.
/// Requests that weren't processed in time or resulted in error are dropped.
pub fn get_last_texture(&self) -> Option<MemoryTexture> {
self.state.last_texture.lock().unwrap().deref().clone()
}
}
// Implement the default constructor for RenderThread using the `new` method.
impl Default for RenderThread {
@ -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
}
}

View File

@ -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

7
lact/benches/bench.rs Normal file
View File

@ -0,0 +1,7 @@
fn main() {
// Include crates in the binary
let _ = lact_daemon::run;
let _ = lact_gui::run;
divan::main();
}