From f2d811172146e96a7728a872007bf753e4ad89a1 Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Thu, 20 Aug 2026 11:25:44 -0600 Subject: [PATCH] =?UTF-8?q?perf(qt):=20instrument=20the=20albums=20load=20?= =?UTF-8?q?=E2=80=94=20sql,=20map,=20serialize?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The albums document is republished on every mount of Local Library (five times in a 37-second screencast) and there was NO instrument anywhere on that path, so "the grid got slower" could only be argued about rather than measured. Three segments, because three different things could cost: the SQL (which now ATTACHes two mirrors and UNIONs three sources), the row mapping (which resolves an ArtRef per row since stage 4), and the JSON handed to QML. Measured on the owner's library, release build: 1703 rows (local + Plex + Jellyfin) sql 59-88ms map 67-71ms ser 2ms 1271 rows (local + Plex) sql 42-88ms map 50-117ms 29 rows (local only) sql 3-7ms map 0.06ms The two large configurations overlap within noise: 432 more rows cost nothing measurable in Rust, and serialisation of 593 KB is 2 ms. Whatever the grid pays for row count is paid AFTER this line — the QML parse and the delegate rebuild — which is exactly what Rust could not see before and what these numbers now bound. cc --- crates/qbz-qt/src/local_albums.rs | 17 +++++++++++++++++ crates/qbz-qt/src/local_bridge_ops.rs | 14 +++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/crates/qbz-qt/src/local_albums.rs b/crates/qbz-qt/src/local_albums.rs index 6b21011df..be23adb13 100644 --- a/crates/qbz-qt/src/local_albums.rs +++ b/crates/qbz-qt/src/local_albums.rs @@ -32,6 +32,14 @@ use crate::local_state::{group_mode, state, with_art, with_db, TRACKS_PAGE}; /// Albums tab: the FULL metadata/folder-grouped set (local + Plex) in one /// page. Search, sort and grouping derive QML-side over the parsed array. pub fn load_albums_blocking() -> Result, String> { + // TIMED, in three segments, because this document is republished on EVERY + // navigation (5 times in a 37-second screencast) and there was no + // instrument on the path at all — so "the grid got slower" could only be + // argued about. The segments are the three things that can actually cost: + // the SQL (which now ATTACHes two mirrors and UNIONs three sources), the + // row mapping (which resolves an ArtRef per row), and the JSON the bridge + // hands to QML. + let t0 = std::time::Instant::now(); let mode = group_mode(); let plex_path = crate::local_plex::cache_db_path(); // The SHARED remote mirror, and which of its sources may show. Both gates @@ -56,14 +64,23 @@ pub fn load_albums_blocking() -> Result, String> { ) }) .ok_or_else(|| "local library not available".to_string())?; + let t_sql = t0.elapsed(); let total = page.total; let albums = page.albums; + let n = albums.len(); + let t1 = std::time::Instant::now(); let rows = with_art(|art| { albums .into_iter() .map(|a| map_album(a, art)) .collect::>() }); + log::info!( + "[qbz-qt][perf] albums load: {n} rows — sql {t_sql:?}, map {:?} (plex={} remote={:?})", + t1.elapsed(), + plex_path.is_some(), + remote_words, + ); state(|s| { s.counts.albums = total as i64; s.albums = rows.clone(); diff --git a/crates/qbz-qt/src/local_bridge_ops.rs b/crates/qbz-qt/src/local_bridge_ops.rs index 0d2165383..e7bd9e7bc 100644 --- a/crates/qbz-qt/src/local_bridge_ops.rs +++ b/crates/qbz-qt/src/local_bridge_ops.rs @@ -161,8 +161,20 @@ pub(crate) fn load_albums() { let _ = tokio::task::spawn_blocking(lib::load_counts_blocking).await; match result { Ok(rows) => { + // TIMED alongside the SQL/map segments in `load_albums_blocking`. + // This document is republished on every mount of the view, and + // the three numbers together are the only way to tell whether a + // slow grid is the query, the mapping, the serialisation, or — + // as it turned out — the QML parse that follows this line and + // that Rust cannot see at all. + let t = std::time::Instant::now(); let json = lib::to_json(&rows); - log::info!("[qbz-qt] local albums published: {} ({} bytes)", rows.len(), json.len()); + let ser = t.elapsed(); + log::info!( + "[qbz-qt][perf] local albums published: {} rows, {} bytes, serialize {ser:?}", + rows.len(), + json.len() + ); ui(move |mut b| { b.as_mut() .set_local_albums_json(QString::from(json.as_str()));