Files
Marcos Paulo 1806455cd9
ci / ci (macos-latest) (push) Has been cancelled
ci / ci (ubuntu-latest) (push) Has been cancelled
ci / ci (windows-latest) (push) Has been cancelled
perf: persistent index cache and a calendar scan cache
Index: .notescraft/cache/index-v1.bin keeps per-note link info, search doc, knowledge passages and lint votes keyed by (mtime, length), re-checked by stat at import; changed notes re-read; corrupt, truncated, oversized or old caches ignored and replaced. The knowledge vocabulary is trusted only by file hash (the old note-count check made a second open slower than a cold one). mdn-docs (14.7k notes, release): second open 1.4 s instead of 15.7-21 s cold.

Calendar: a per-session cache of what each note's scan gave the agenda (parsed events, all dated tasks) validated by length and mtime; window switches only filter in memory (mdn-docs 695 ms -> 2.7 ms), the cold pass runs in 8 ms slices across frames while the old grid stays visible, the week review reuses loaded items.

Built by Claude Sonnet; verified from a clean checkout of the staged files: 1901 tests in engine, index, knowledge and ui-egui lib plus calendar_view pass, clippy -D warnings on the four crates, xtask layers, tree and i18n clean.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 21:10:39 -03:00

49 lines
2.1 KiB
Rust

//! Calendar window switches on a big vault (run on demand, release build):
//! `cargo test --release -p notescraft-engine --test perf_calendar -- --ignored --nocapture`.
//!
//! The first call is what every call cost before the scan cache; the later ones are window switches.
use std::path::PathBuf;
use std::time::{Duration, Instant};
use notescraft_engine::{Session, agenda_items, agenda_items_cached, agenda_restart, agenda_warm};
fn day(text: &str) -> notescraft_templates::Date {
text.parse::<notescraft_calendar::EventTime>().expect("date").date()
}
#[test]
#[ignore = "measurement, run on demand in a release build"]
fn window_switches_on_a_big_vault() {
let Some(home) = std::env::var_os("HOME") else { return };
for name in ["mdn-docs", "k8s-docs", "obsidian-help-all"] {
let root = PathBuf::from(&home).join("Documents/vaults").join(name);
if !root.is_dir() {
continue;
}
let Ok(session) = Session::open_read_only(&root) else { continue };
let timed = |label: &str, work: &dyn Fn() -> usize| {
let start = Instant::now();
let items = work();
println!("{name:<18} {label:<34} {:>10.2?} ({items} items)", start.elapsed());
};
let (a, b, c, d) = (day("2026-10-01"), day("2026-10-31"), day("2026-10-19"), day("2026-10-25"));
timed("first agenda_items (cold)", &|| agenda_items(&session, a, b).0.len());
timed("second agenda_items, other window", &|| agenda_items(&session, c, d).0.len());
timed("agenda_items_cached, third window", &|| agenda_items_cached(&session, a, d).0.len());
agenda_restart(&session);
let start = Instant::now();
let (mut frames, mut slowest) = (0, Duration::ZERO);
loop {
let frame = Instant::now();
let done = agenda_warm(&session, Duration::from_millis(8));
slowest = slowest.max(frame.elapsed());
frames += 1;
if done {
break;
}
}
println!("{name:<18} {:<34} {:>10.2?} ({frames} frames, slowest {slowest:.2?})", "warm pass at 8 ms/frame", start.elapsed());
}
}