Do not recount face coverage on every confirm, and count it without listing
Every click on the Identity screen's face grid — confirm, reject, split, rename, merge — redrew the whole screen, and the redraw recomputed the coverage line. That line lists every repair's outstanding images to count them: six scans of the images table with a correlated EXISTS over the 8 KB face rows, an ORDER BY the job's visiting order, a Target with its path per row, and a thumbnail-index query per image with faces. On the reference library (24k images, 19k faces) that was ~200 ms of the ~540 ms each click cost, spent computing a figure a confirm cannot change. `refresh` now takes what changed: `Changed::Identities` re-reads the rail and the grid and leaves the coverage line alone; `Changed::Library` — an open, a sweep ending or stopped, the face data deleted — re-reads it too. For the times it does run, `repairs::counts` counts instead of building and dropping the lists, and the thumbnail store is read once (`ThumbStore::held`) rather than probed once per image in the audit, the outstanding list and the proxy repair. `identity_bench` is the measurement: the reads a click performs and the batch writes, timed against a copy of a real catalog.
This commit is contained in:
+17
-2
@@ -116,6 +116,12 @@ pub fn faces_outstanding(
|
||||
ORDER BY i.id",
|
||||
faces::embedder_sql("fi.model_id")
|
||||
))?;
|
||||
// The store's index in one read rather than a probe per image; see
|
||||
// `audit`, which splits the same list the same way.
|
||||
let held = store.held(FACE_TIER).unwrap_or_else(|e| {
|
||||
log::warn!("faces: reading the thumbnail index: {e}");
|
||||
Default::default()
|
||||
});
|
||||
let rows = stmt
|
||||
.query_map([faces::embedder_of(model_id)], |r| {
|
||||
Ok(FaceRequest {
|
||||
@@ -134,7 +140,7 @@ pub fn faces_outstanding(
|
||||
// a whole-library button that could only reach photographs the user had
|
||||
// personally zoomed into. `repairs::spawn` is that
|
||||
// requirement implemented; this one is the local-only variant.
|
||||
.filter(|req| store.contains(req.file_id, FACE_TIER))
|
||||
.filter(|req| held.contains(&req.file_id))
|
||||
.collect();
|
||||
Ok(rows)
|
||||
}
|
||||
@@ -270,12 +276,21 @@ pub fn audit(
|
||||
)",
|
||||
faces::embedder_sql("fi.model_id")
|
||||
))?;
|
||||
// One read of the store's index, not one probe per outstanding image:
|
||||
// `contains` answers the same question, and asked four thousand times
|
||||
// it cost more than every query above put together. A store whose index
|
||||
// cannot be read is treated as holding nothing, which is what `contains`
|
||||
// reports for it too.
|
||||
let held = store.held(FACE_TIER).unwrap_or_else(|e| {
|
||||
log::warn!("faces: reading the thumbnail index: {e}");
|
||||
Default::default()
|
||||
});
|
||||
let (mut ready, mut awaiting) = (0u64, 0u64);
|
||||
for file_id in stmt
|
||||
.query_map([faces::embedder_of(model_id)], |r| r.get::<_, i64>(0))?
|
||||
.filter_map(Result::ok)
|
||||
{
|
||||
if store.contains(file_id as u64, FACE_TIER) {
|
||||
if held.contains(&(file_id as u64)) {
|
||||
ready += 1;
|
||||
} else {
|
||||
awaiting += 1;
|
||||
|
||||
Reference in New Issue
Block a user