perf(files): background load, parse-free clock scan - #1182
Conversation
Opening a single org file blocked until every agenda file was read. The startup clock scan walks the headlines of all files, which forces a treesitter parse of each one, so time to interaction grew with the size of the agenda. Loading now starts in the background and the startup clock scan waits for it instead of blocking. Callers that need all files still wait at the existing barrier, so the cost is paid when the agenda is opened rather than when a file is edited. The alternative, keeping the scan synchronous and only deferring the file reads, was dropped because the parse is the expensive part.
Finding the active clock parsed every agenda file with treesitter. That parse blocked the event loop on startup and after every clock action, longer with each added file. A cheap pre-filter now picks the files worth parsing: a raw text scan for CLOCK lines that could still be open. False positives will be caught by the parser, which decides what is clocked in. CLOCK matches case insensitively, so hand written lowercase entries keep working.
|
|
|
Measured on 500 generated org-roam style files (1.8 MB, one running clock in the last file), median of 3 runs:
Script: orgbench.lua. |
|
Ok, thanks for the benchmark. We can go with it for now, but I'm hoping to find a better solution in the future for tracking clocked in headline. |
ref #1048, replaces #1073
Opening a single org file waits until every agenda file has been read. The startup clock scan walks the headlines of every file, and that parses each one with tree-sitter. With a few hundred org-roam files that is seconds of blocked editor.
The parse is the expensive part, not the read; deferring the reads alone does not help. Two independent commits:
The clock scan parses only candidate files. A running clock is a CLOCK line with a single timestamp and nothing after it. A raw line scan picks the files that have one; only those get parsed, and the parser still decides. Archive files are skipped.
Loading moves to the background.
Org:initno longer waits forfiles:load();Clock:initchains on the load promise, and a secondload()call during loading gets that promise instead of blocking. Callers that need all files still wait atensure_loaded. The cost moves to the first agenda open.