mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-27 11:37:13 -06:00
perf(engine): #1136 album order and album commands without per-photo scans
A manual album shown in its own order sorted with position() per photo (O(n^2): 2.0 s for 20k photos, now 18 ms). album.addPhotos, removePhotos and toggleTarget test membership with sets. The 100k scale test measures all three. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
615d8a365e
commit
3f3ffa541a
@@ -1147,8 +1147,10 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
let (targets, skipped) = album_targets_or_err(s, "album.addPhotos", targets)?;
|
||||
let mut photos = al.photos.clone();
|
||||
let before = photos.len();
|
||||
// looked up, not searched for: adding thousands to a big album stays linear
|
||||
let mut held: std::collections::HashSet<PhotoId> = photos.iter().copied().collect();
|
||||
for t in targets {
|
||||
if !photos.contains(&t) {
|
||||
if held.insert(t) {
|
||||
photos.push(t);
|
||||
}
|
||||
}
|
||||
@@ -1179,11 +1181,14 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
}
|
||||
};
|
||||
let al = s.catalog.album(id).ok_or_else(|| bad("album.toggleTarget", "no target album"))?;
|
||||
let all_in = !targets.is_empty() && targets.iter().all(|t| al.photos.contains(t));
|
||||
// looked up, not searched for: thousands toggled in a big album stay linear
|
||||
let mut held: std::collections::HashSet<PhotoId> = al.photos.iter().copied().collect();
|
||||
let all_in = !targets.is_empty() && targets.iter().all(|t| held.contains(t));
|
||||
let photos: Vec<PhotoId> = if all_in {
|
||||
let targets: std::collections::HashSet<PhotoId> = targets.iter().copied().collect();
|
||||
al.photos.iter().copied().filter(|x| !targets.contains(x)).collect()
|
||||
} else {
|
||||
al.photos.iter().copied().chain(targets.iter().copied().filter(|t| !al.photos.contains(t))).collect()
|
||||
al.photos.iter().copied().chain(targets.iter().copied().filter(|t| held.insert(*t))).collect()
|
||||
};
|
||||
let name = al.name.clone();
|
||||
let cover = al.cover.filter(|c| photos.contains(c)).or(photos.first().copied());
|
||||
@@ -1317,6 +1322,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
if al.is_smart() {
|
||||
return Err(bad("album.removePhotos", "smart albums update automatically: change their rules"));
|
||||
}
|
||||
let targets: std::collections::HashSet<PhotoId> = targets.into_iter().collect();
|
||||
let photos: Vec<PhotoId> = al.photos.iter().copied().filter(|x| !targets.contains(x)).collect();
|
||||
s.commit("Remove from Album", Op::SetAlbumPhotos { id, photos })?;
|
||||
ok()
|
||||
|
||||
@@ -874,8 +874,12 @@ impl Session {
|
||||
&& !al.is_smart()
|
||||
&& self.filter == Filter::default()
|
||||
{
|
||||
let order = al.photos.clone();
|
||||
visible.sort_by_key(|id| order.iter().position(|x| x == id).unwrap_or(usize::MAX));
|
||||
// each photo's place looked up, not searched for (a big album sorted in O(n log n))
|
||||
let mut place: std::collections::HashMap<PhotoId, usize> = std::collections::HashMap::with_capacity(al.photos.len());
|
||||
for (i, id) in al.photos.iter().enumerate() {
|
||||
place.entry(*id).or_insert(i);
|
||||
}
|
||||
visible.sort_by_key(|id| place.get(id).copied().unwrap_or(usize::MAX));
|
||||
if !self.sort.ascending {
|
||||
visible.reverse();
|
||||
}
|
||||
|
||||
@@ -440,6 +440,21 @@ fn scale_100k_library_queries() {
|
||||
m("undo", &mut || {
|
||||
s.execute("edit.undo", &serde_json::json!({})).unwrap();
|
||||
});
|
||||
// a 20k-photo album shown in its own order (capture-date sort, no filter)
|
||||
let big = s.execute("album.create", &serde_json::json!({"name": "Big"})).unwrap()["id"].as_u64().unwrap();
|
||||
let ids: Vec<u64> = (0..n).rev().step_by(5).map(|i| i + 1).collect();
|
||||
m("add 20k to an album", &mut || {
|
||||
s.execute("album.addPhotos", &serde_json::json!({"id": big, "ids": ids})).unwrap();
|
||||
});
|
||||
m("album of 20k (album order)", &mut || {
|
||||
s.execute("library.source", &serde_json::json!({"kind": "album", "id": big})).unwrap();
|
||||
let _ = s.visible_cloned();
|
||||
});
|
||||
s.execute("library.source", &serde_json::json!({"kind": "all"})).unwrap();
|
||||
let half: Vec<u64> = ids.iter().copied().step_by(2).collect();
|
||||
m("remove 10k from the album", &mut || {
|
||||
s.execute("album.removePhotos", &serde_json::json!({"id": big, "ids": half})).unwrap();
|
||||
});
|
||||
worst.sort_by(|a, b| b.1.total_cmp(&a.1));
|
||||
eprintln!("slowest: {:?}", &worst[..3]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user