Files
mapsize/PERFORMANCE.md
T

118 lines
4.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.
# Performance
All numbers below were measured, not estimated. Re-run them on your own
hardware before relying on them; filesystem caches dominate scan timings.
## Test machine (2026-10-08)
| | |
|---|---|
| CPU | Intel Core i7-7700 @ 3.60 GHz, 4 cores / 8 threads |
| RAM | 32 GiB |
| OS | Linux 7.0, Rust 1.91.1 (release profile: LTO, one codegen unit) |
| Filesystem | ext4 on Linux software RAID (`/dev/md2`) over two 7200 rpm SATA HDDs |
The host is shared; its load average was 5–9 during these runs. Each figure
is the median (and best) of 7 runs.
## Scanner
Dataset: a source-package cache directory: 388,307 files + 65,518
directories (458,510 inventory nodes), 10.5 GiB allocated. **Warm cache
only**: dropping the page cache needs root, so these measure CPU and syscall
cost, not disk seeks. Cold-cache behaviour on HDDs will be dominated by seek
time; expect fewer workers to be optimal there.
`mapsize --no-ui --workers N PATH`:
| workers | median | best | peak RSS |
|---:|---:|---:|---:|
| 1 | 1.52 s | 1.44 s | 75 MB |
| 8 (balanced default) | 0.47 s | 0.43 s | 76 MB |
| 32 | 0.46 s | 0.43 s | 80 MB |
At 8 workers that is about 1,000,000 entries a second, in under 80 MB for
458k nodes. Most of the time is spent in the `statx` and `getdents` system
calls.
| other runs over the same tree | median | peak RSS |
|---|---:|---:|
| scan + `--largest-files 50` | 0.60 s | 76 MB |
| scan + `--json` (streamed to /dev/null) | 0.86 s | 76 MB |
## Snapshots
| operation | median | peak RSS |
|---|---:|---:|
| scan + save | 0.75 s | 77 MB |
| load (`mapsize --no-ui file.msz`) | 0.23 s | 78 MB |
| load a snapshot written by mapsize 1.2.0 | 0.26 s | 78 MB |
Loading decompresses and verifies SHA-256 on one thread while another parses.
Against a warm cache that is about twice as fast as rescanning; against a
cold cache or a network filesystem, where scans are dominated by I/O
latency, the gain is far larger (not yet measured here).
## UI
`make benchmark` (`examples/bench.rs`), best of 5 runs, on a synthetic
in-memory tree of 1,008,201 nodes (200 directories × 40 subdirectories × 100
files, plus one flat directory with 200,000 files), 200×60 terminal:
| operation | time per op |
|---|---:|
| cold frame at root (sort children, LOD, layout incl. nested previews, paint) | 11.34 ms |
| resize to a new size (layout + paint, child lists cached) | 1.50 ms |
| arrow-key move + re-render | 1.31 ms |
| zoomed into the 200,000-entry directory, cold | 9.37 ms |
During a scan the size caches are invalidated at most every 400 ms, so even
a cold frame costs a few percent of one core. Resize events only record the
new size; layout happens once per rendered frame, so a resize storm cannot
queue up work.
### Geometry
| operation | time |
|---|---:|
| squarified layout, 300 items | 8.4 µs |
| spatial neighbour lookup among 300 blocks | 1.1 µs |
### Filtering (1,008,201 nodes)
| query | time |
|---|---:|
| `size > 1MB` | 26.75 ms |
| `*.dat AND size > 500k` | 67.84 ms |
| `path contains sub3` | 63.80 ms |
Filters run on a background thread with cancellation and a 120 ms keystroke
debounce; the UI never waits for them. Path predicates build each path from
the cached parent directory path into a reused buffer, and `*.ext` globs are
a suffix test.
## Compared with mapsize 1.2.0
mapsize 1.2.0 was the last release of the previous implementation, written
in Go. Both were run alternately on the same machine, over the same tree, in
the same session:
| | 1.2.0 | current | |
|---|---:|---:|---|
| scan, 8 workers (median) | 0.63 s | 0.47 s | 1.34× faster |
| scan, 1 worker (median) | 1.89 s | 1.52 s | 1.24× faster |
| peak RSS while scanning | 117 MB | 76 MB | |
| scan + save snapshot | 1.22 s | 0.75 s | 1.63× faster |
| load snapshot | 0.34 s | 0.23 s | 1.48× faster |
| scan + JSON export | 1.27 s, 153 MB | 0.86 s, 76 MB | 1.48× faster |
| cold frame, 1M nodes | 36.01 ms | 11.34 ms | 3.18× faster |
| squarified layout, 300 items | 84.3 µs | 8.4 µs | 10.07× faster |
| arrow key + re-render | 1.50 ms | 1.31 ms | 1.14× faster |
| filter `*.dat AND size > 500k` | 121.40 ms | 67.84 ms | 1.79× faster |
## Known costs / future work
* Node records could shrink (owner/group/mode/link count are only needed for
display and some filters) if memory on 10 M+ entry trees matters.
* Cold-cache HDD benchmarks still need to be run with root access.