Files
Marcos Paulo 5e8020aa38
ci / ci (macos-latest) (push) Has been cancelled
ci / ci (ubuntu-latest) (push) Has been cancelled
ci / ci (windows-latest) (push) Has been cancelled
feat: Obsidian basics, editor previews, graph rework, query blocks, stacked tabs, pop-out windows
Editor previews (colour swatches, arrows, embeds), the 18 obsidian-basics tasks (find and replace
with regex, back/forward, auto-pair, footnotes, folding, search operators, split view, PDF/audio/
video embeds, spelling, drag and drop, pinned tabs, settings) and round 2: ```query blocks, stacked
tabs, pop-out windows, tasks ticked inside embeds, text around an embed, embeds that follow edits,
block menu shortcut. New crates: notescraft-regex (linear-time engine) and notescraft-syntax.

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

66 lines
3.2 KiB
Rust

//! 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<T>(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<String> = 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::<Vec<_>>());
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:?}");
}