//! Open-time measurements for large vaults (run on demand, release build): //! `cargo test --release -p notescraft-engine --test perf_open -- --ignored --nocapture`. //! //! Prints where the time goes when a vault is read: the file list, reading the files, and each //! index that is fed with a note. The numbers decide whether an index cache is worth having. mod common; use std::time::{Duration, Instant}; use common::temp_vault; use notescraft_engine::Session; const NOTES: usize = 5000; const FOLDERS: usize = 50; fn body(n: usize) -> String { let next = (n + 1) % NOTES; format!( "---\ntags: [t{}]\n---\n# Note {n}\n\nParagraph about topic {} and project {} with [[n{next}]] and a #tag{}.\n\n- [ ] task {n}\n- item two\n\n## Section\n\nMore words: alpha beta gamma delta epsilon zeta eta theta iota kappa {n}.\n", n % 50, n % 97, n % 31, n % 13 ) } fn timed(label: &str, work: impl FnOnce() -> T) -> (T, Duration) { let start = Instant::now(); let out = work(); let took = start.elapsed(); println!("{label:<34} {took:>10.2?}"); (out, took) } #[test] #[ignore = "measurement, run on demand in a release build"] fn where_the_time_goes_when_a_vault_is_read() { let vault = temp_vault("perf_open"); for n in 0..NOTES { let folder = vault.path().join(format!("f{}", n % FOLDERS)); std::fs::create_dir_all(&folder).expect("folder"); std::fs::write(folder.join(format!("n{n}.md")), body(n)).expect("note"); } println!("--- {NOTES} notes in {FOLDERS} folders"); let (listing, _) = timed("list the vault (names, sizes)", || notescraft_vault::Vault::open_read_only(vault.path()).and_then(|v| v.list()).expect("list")); let paths: Vec = listing.into_iter().filter(|e| !e.is_dir).map(|e| e.path).collect(); let (texts, _) = timed("read every note", || paths.iter().map(|p| std::fs::read_to_string(vault.path().join(p)).expect("read")).collect::>()); let mut links = notescraft_index::Index::new(); timed("link index: update_note", || paths.iter().zip(&texts).for_each(|(p, t)| links.update_note(p, t).expect("index"))); let mut search = notescraft_index::search::SearchIndex::new(); timed("search index: update", || paths.iter().zip(&texts).for_each(|(p, t)| search.update(p, t).expect("search"))); let mut passages = notescraft_knowledge::Index::default(); timed("knowledge passages: update", || paths.iter().zip(&texts).for_each(|(p, t)| drop(passages.update(p, t)))); let mut vocab = notescraft_knowledge::Vocab::default(); timed("knowledge vocabulary: update", || paths.iter().zip(&texts).for_each(|(p, t)| drop(vocab.update(p, t)))); let (mut session, lazy) = timed("open_lazy (names only)", || Session::open_lazy(vault.path()).expect("lazy")); let ((), scan) = timed("index every note in steps", || while !session.index_step(Duration::from_millis(8)).finished {}); drop(session); let (_, eager) = timed("eager open (whole thing)", || Session::open(vault.path()).expect("eager")); println!("--- first window after {lazy:.2?}; everything indexed after {scan:.2?} more; eager open {eager:.2?}"); assert!(lazy < Duration::from_secs(1), "the first window should not wait for the notes: {lazy:?}"); }