perf(qt): instrument the albums load — sql, map, serialize
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
This commit is contained in:
2 files changed
+30
-1
No files matched your search
@@ -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<Vec<AlbumRow>, 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<Vec<AlbumRow>, 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::<Vec<AlbumRow>>()
|
||||
});
|
||||
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();
|
||||
|
||||
@@ -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()));
|
||||
|
||||
Reference in new issue
Block a user