Performance and optimization evidence¶
The 2026-09-05 release-profile measurements show roughly 25–30% lower median latency for the large filename-discovery, glob and bulk-delete workloads after this optimization. The strongest repeatable gain is less CPU and allocation work inside execution—not skipping snapshots, policy, dependency checks or durability.
These are local macOS arm64 results for the VSH 0.4.0 Monty 0.0.22 implementation, not cross-platform guarantees or service-level objectives.
Measurement protocol¶
- Host: macOS 26.1, Apple arm64, 8 logical CPUs; CPython 3.14.6.
- Both the native harness and PyO3 extension use optimized release builds and a matching supervised worker. No in-process execution shortcut.
- Per run: 40 retained warm samples per case after one discarded warmup; 20 independent cold-runtime samples; four independent runtimes in the concurrency case.
- Large fixture: 100 directories with 100 files each. Small fixture: 20 input files.
- Baseline captured before runtime optimization. Final and independent confirmation runs use the same workloads, counts, decisions and limits.
- Timed cases are previews, including durable pending-approval storage where required; they do not measure actual application/commit latency.
Raw JSON, environment details, intermediate results and caveats live in
benchmarks/results/2026-09-05/.
The generated comparison.json / comparison.md include every case, p95 values,
confirmation runs and stage distributions. An initial debug-extension diagnostic
is retained but explicitly excluded from the release comparison.
Warm preview latency¶
Milliseconds, p50. Before → final; negative outcomes have not been removed.
| Workload | Rust before → final | Python before → final |
|---|---|---|
| No-op | 0.267 → 0.167 | 0.181 → 0.287 |
| Read 10 files | 0.869 → 0.887 | 0.861 → 1.024 |
| Edit 20 files | 1.856 → 1.943 | 1.840 → 1.774 |
| Filter names in 10,000-file tree | 67.922 → 49.002 | 68.130 → 48.689 |
vsh_glob in 10,000-file tree |
71.975 → 54.002 | 73.716 → 51.964 |
| Rename 100-file subtree | 56.976 → 55.962 | 58.011 → 55.977 |
| Remove 100-file subtree with typed OS calls | 47.952 → 46.048 | 48.875 → 45.981 |
vsh_remove of 100-file subtree |
48.012 → 45.082 | 47.801 → 45.057 |
| Remove 5,000 files + 50 directories | 175.077 → 131.162 | 182.400 → 130.868 |
The name-filter case knows the fixture has two directory levels and returns a count. The glob case uses generic recursive matching and returns 1,000 typed paths. Neither is a text-content search benchmark, and they are not identical result contracts.
Confirmation and tail behavior¶
An independent repeat of the optimized binary measured:
| Workload | Rust repeat p50 / p95 | Python repeat p50 / p95 |
|---|---|---|
| Filename filtering | 50.677 / 52.319 | 49.045 / 51.466 |
| Generic glob | 57.591 / 63.819 | 51.775 / 52.185 |
| Bulk delete preview | 138.012 / 147.617 | 133.015 / 138.733 |
The large-workload gain repeats, though its magnitude varies. Small calls are sensitive to scheduling and filesystem state: Python's no-op/read repeat was 0.153/0.886 ms, while the first final run was 0.287/1.024 ms. The Rust confirmation's durable rename case also rose to 66.967 ms. No general small-call, durable-I/O or tail-latency improvement is claimed. These sequential local runs are not randomized A/B trials.
Where time was removed¶
Rust p50 stage times, milliseconds:
| Stage within workload | Before | Final |
|---|---|---|
| Filename-filter execution | 34.893 | 17.429 |
| Glob execution | 36.445 | 19.765 |
| Bulk-delete execution | 101.734 | 69.890 |
| Bulk-delete canonical diff | 5.657 | 4.321 |
| Bulk-delete final policy evaluation | 11.600 | 3.534 |
| Filename-filter snapshot | 32.048 | 30.621 |
Call-policy checks performed during traversal are charged to execute, not just the
final policy stage. Stage medians are measured independently and need not sum to the
wall-time median. Durable binding/storage remains real work; it was not bypassed.
Algorithms and data structures¶
| Change | Removed work | Preserved contract |
|---|---|---|
| Cursor-based policy globstar matcher | Per-path component vectors and one DP allocation per pattern component | Same matching language, canonical rules and first denial |
| Leading-globstar basename specialization | Scanning parent components for **/*.key-style rules |
Same root and nested-path semantics |
Canonical VPath fast path |
Separator replacement/vector/join for already canonical inputs | Portable normalization and rejection priority |
| Borrowed ancestor lookups | Allocating a new owned path for every ancestor probe | Tombstone and non-directory shadowing |
| Empty-overlay resolution/listing | Parent probes, temporary tree and redundant visibility lookups | Immutable base visibility |
| Slash-bounded overlay range | Scanning every unrelated overlay path for a directory listing | Exact component boundaries and sorted results |
| Fused snapshot indexing / entry insertion | Duplicate parent derivation and B-tree searches | Parent validation order and snapshot identity |
| Flat canonical-diff after-state buffer | Extra keyed tree and cloned path keys | Complete lazy after-materialization before reading before-states |
The policy matcher uses constant auxiliary memory and no recursion. It performs at most O(pattern components × path components) component matches; wildcard work inside each component is separate. Overlay child discovery visits the relevant lexical subtree, not an unrelated whole overlay, and retains no extra permanent child index.
A separate 10,000-path microbenchmark, with 1,000 expected denials and ten retained samples, measured policy authorization median 17.875 → 5.806 ms (67.5% lower) and canonical parsing 1.119 → 0.712 ms (36.4% lower). It isolates these costs; it is not an end-to-end latency claim or an allocation-profiler trace.
Correctness checks compare the new matcher against the original dynamic-programming oracle over 94,501 combinations, including empty paths and multiple globstars, plus deep paths and compiled fast paths. Portable-path oracle checks, prefix-sibling listing tests and generated VFS operation sequences protect normalization and diff semantics.
CPU and memory costs¶
Command-reported user + system time for the complete harness, including fixture, cold and concurrency work, fell from 18.82 to 15.27 seconds for Rust and 18.93 to 15.25 seconds for Python. This is about 19% lower reported CPU time for that matrix, not an isolated per-transaction CPU measurement or a billing estimate.
Separate process-tree RSS sampling every 50 ms recorded:
| Surface | Root peak MiB before → final | Summed tree peak MiB before → final | Max observed processes |
|---|---|---|---|
| Rust | 36.31 → 33.42 | 56.09 → 42.22 | 5 → 2 |
| Python | 53.86 → 57.81 | 77.78 → 97.28 | 5 → 7 |
These samples do not establish a repeatable RSS reduction or regression. The
sampler missed different short-lived overlaps; summing RSS also double-counts shared
pages and is not unique memory/PSS. Python's sampled root peak increased despite
lower temporary allocation work. Command-reported RSS high-water marks use another
scope and are retained separately in command-rusage.json.
The implementation eliminates specific temporary allocations; a universal resident memory win has not been demonstrated. No model calls, token prices, storage retention costs or monetary savings were measured.
Cold startup and parallel work¶
The final run's 20 independent cold samples measured runtime-open p50 of 28.07 ms (Rust) and 28.62 ms (Python), followed by first-call p50 of 4.72/4.79 ms. Keep cold startup separate from warm preview figures. Reuse runtimes when the trusted workspace/configuration is stable.
Four independent runtimes measured 3.27× native and 3.30× Python throughput speedup over the harness's sequential phase. This demonstrates useful concurrency in this workload, not guaranteed linear scaling or same-workspace commit throughput.
Reproduce¶
Build the current release extension and matching worker using development. Run the two surfaces sequentially, without simultaneous tests, compilers or memory samplers:
cargo build --release --locked -p vsh-runtime --example native_benchmark
target/release/examples/native_benchmark \
--iterations 40 --cold-iterations 20 --parallel-workers 4 \
--worker "$PWD/target/release/vsh-monty-worker" --output native-rust.json
VSH_MONTY_WORKER="$PWD/target/release/vsh-monty-worker" \
uv run --no-sync python benchmarks/native_pyo3.py \
--iterations 40 --cold-iterations 20 --parallel-workers 4 --output native-python.json
For separate memory instrumentation:
uv run --no-sync python benchmarks/process_tree.py --output memory.json -- \
target/release/examples/native_benchmark \
--iterations 40 --cold-iterations 20 --parallel-workers 4 \
--worker "$PWD/target/release/vsh-monty-worker" --output instrumented.json
Do not use instrumented.json as latency evidence. Rebuild and verify release artifacts
before comparing; the first diagnostic in this session caught a debug extension that
would otherwise have produced a misleading speedup claim.
Remaining costs and deliberate boundaries¶
Fresh metadata traversal still costs about 30–31 ms on this large fixture. Reducing the trusted workspace root is an immediate application-level lever. A TTL snapshot cache would weaken freshness and was not introduced. Bulk typed-call loops still pay IPC; use bounded compound functions where their semantics fit. Pending approval and commit still pay real durable I/O and integrity/revalidation costs.
Hosted OS/storage measurements, controlled steady-state process memory, commit/recovery performance and actual agent-loop cost remain separate evidence work. See efficient usage for practical tuning.