kate-deft/docs/PERF.md

82 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Switcher performance — measured bottlenecks
Benchmark harness: `src/project/bench_switcher.cpp` (manual target `bench_switcher`,
not a ctest). Run:
```sh
cmake -S . -B build-rel -DCMAKE_BUILD_TYPE=Release
cmake --build build-rel --target bench_switcher
./build-rel/bin/bench_switcher <project-root> /tmp/switcher_metrics.txt
```
Reference project: `~/prj/r7.20/mobydick` — **12,234 files**, **17,184 ctags
symbols**, 6.3 GB tree.
## One-time cost when the switcher opens (UI thread, synchronous)
| Stage | Release | Debug (shipped) |
|----------------------------|--------:|----------------:|
| `ProjectIndex::listFiles` | ~84 ms | ~78 ms |
| `SymbolIndex::listSymbols` | ~974 ms | ~499 ms |
| build items + `setItems` | ~5 ms | ~45-70 ms |
- `listFiles` is a `git ls-files` subprocess — acceptable but blocks the UI.
- `listSymbols` is a **ctags subprocess (~0.5-1 s)** — the dominant open-cost,
and it blocks the UI thread. (Cost is in the external binary, not our parse.)
## Per-keystroke cost — the interactive bottleneck
`PaletteModel::setQuery` → re-rank. Originally O(N) every keystroke; the cost
*grew* with query length even as the result set shrank.
Full-scan (every keystroke re-scores all N), Release:
| Query | files | symbols |
|------------------|---------:|---------:|
| `server` | ~14 ms | ~30 ms |
| `src server go` | ~17 ms | ~36 ms |
Debug (what ships): **270-342 ms (files)**, **459-598 ms (symbols)** per
keystroke — unusable.
## Fix 1 — incremental narrowing (done)
When a query only **appends** to the previous one, re-score only the currently
visible subset (the match set is monotonic under lengthening/adding a needle),
turning later keystrokes from O(N) into O(previous matches). Exactness is
guarded by `test_palettemodel::incrementalMatchesFullScan` (incremental ==
full-scan, incl. the typo tier).
Effect once the set narrows (Release, type-forward):
| Query | full-scan | incremental |
|----------|----------:|------------:|
| files `server` | ~14 ms | **~6.6 ms** |
| symbols `server` | ~30 ms | **~9.0 ms** |
Early keystrokes (`s`, `se`) remain full-N because almost everything matches —
correct and unavoidable without a prefix index.
## Fix 2 — build the installed plugin optimized (recommended)
The shipped `olliepalette.so` is a **Debug** build (`-g`, no `-O`). Release is
~5-8× faster on every hot path above. Install a `Release` or `RelWithDebInfo`
build.
## Follow-up — move indexing off the UI thread (done)
`ProjectIndexer` (`src/project/projectindexer.{h,cpp}`, in `project_lib`, 6
tests) runs `listFiles` / `listSymbols` on the thread pool (`QtConcurrent` +
`QFutureWatcher`) and delivers results on the UI thread via `filesReady` /
`symbolsReady`, caching per project root. The switchers now:
- open the palette **immediately** (cache hit → instant; miss → a transient
"Indexing…" / "Indexing symbols…" placeholder), and
- repopulate via the ready signal when the background job finishes.
A per-root generation counter drops superseded/invalidated results. The cache
is dropped on `KateProjectBridge::projectChanged` so a project switch re-indexes.
Net effect: the UI thread never blocks on git/ctags; the ~1 s Alt+G stall is
gone (first open shows a placeholder and fills in; subsequent opens are instant
from cache).