Skip to content
nobspromptby neg4n.dev
Esc
navigateopen⌘Jpreview
On this page

Performance and memory

Reproduce foreground latency measurements and understand the native safety checks, their coverage, and their limits.

The foreground nbsp data command is the collector used by both opinionated and detached Zsh. Git status refresh is deliberately outside that path: Zsh starts nbsp refresh as a background worker and reloads prompt data after the worker completes.

The repository does not ship a numerical performance baseline. Do not quote a latency, binary-size improvement, memory footprint, or instruction-count claim without retaining the raw report and enough build and machine metadata to reproduce the comparison.

Benchmark protocol

bench/benchmark.zsh measures a chosen command with one fresh nbsp process per sample. Its defaults are 100 warm-up invocations and 1,000 measured invocations. For example:

zsh bench/benchmark.zsh \
  --warmup 100 \
  --iterations 3000 \
  --output ./data-benchmark.txt \
  -- ./build/nbsp data

The harness supports data and dirs. It performs the requested warm-up invocations first, times each measured invocation with Zsh’s EPOCHREALTIME, redirects the command’s standard output, and calculates mean, minimum, maximum, p50, and p95 from the sorted samples. Percentiles use the nearest-rank index. The report also contains every sample as sample_sorted_ms; those values cannot reconstruct temporal order. Because EPOCHREALTIME is a wall clock rather than a monotonic clock, the harness rejects negative, non-finite, and implausibly large intervals but cannot prevent smaller wall-clock adjustments from affecting a result.

For data, the default representative inputs are status 17, duration 2345 milliseconds, and two jobs. NBSP_BENCH_STATUS, NBSP_BENCH_DURATION_MS, and NBSP_BENCH_JOBS select different inputs. Iterations, warm-up, output, and working directory also have NBSP_BENCH_ITERATIONS, NBSP_BENCH_WARMUP, NBSP_BENCH_OUTPUT, and NBSP_BENCH_CWD defaults that their corresponding command-line flags override.

Iterations are limited to 100,000 and warm-up invocations to 100,000. The harness rejects an output symlink, special file, or pathname/inode that would replace the measured binary. It also discards the report if the binary’s version, size, or required SHA-256 changes during the run.

Format-2 reports use key=value lines. Text values use percent-byte encoding so embedded whitespace, newlines, or = cannot create records. Numeric fields and SHA-256 values remain plain ASCII. Each report records:

  • start and end UTC timestamps, uname data, OS build, CPU brand when available, hardware model, and Zsh version;
  • the benchmark script’s SHA-256 plus the revision and dirty state of the checkout containing that script, when it is a Git repository;
  • the binary’s absolute path, reported version, byte size, and mandatory SHA-256;
  • caller-declared binary source revision/dirty state, build type, compiler, and compiler/linker flags, each recorded as unreported unless supplied through the corresponding NBSP_BENCH_* variable;
  • the working directory, mode, shell-quoted invocation, warm-up count, and measured iteration count;
  • a clean-environment workload repository root, HEAD, and coarse clean/dirty state when applicable;
  • NVM_BIN, cache-root inputs, and the effective cache-root candidate;
  • a SHA-256 of a NUL-framed observable workload snapshot captured before the warm-up and again after measurement; the report is discarded if those snapshots differ;
  • the aggregate values and all sorted samples.

NBSP_BENCH_SOURCE_REVISION, NBSP_BENCH_SOURCE_DIRTY, NBSP_BENCH_BUILD_TYPE, NBSP_BENCH_COMPILER, NBSP_BENCH_CFLAGS, and NBSP_BENCH_LDFLAGS are declarations by the caller; the harness cannot prove that an arbitrary selected binary came from its own checkout or from those settings. The workload digest records the collector-visible data (or dirs frame), not raw cache-file bytes or every filesystem attribute. Power and thermal state, competing system load, and temporal sample order are also not captured. Record and hold those conditions constant before comparing builds. A warm repository and a non-repository directory remain different workloads.

Foreground and asynchronous work

The foreground collector starts neither Git nor Node. It still performs native filesystem work: getcwd(3) in the CLI, physical-path canonicalization, repository metadata discovery, direct HEAD reads, bounded cache reads and validation, path abbreviation, and NVM_BIN parsing. Invoking it through Zsh command substitution also starts the nbsp process itself.

The optional autosuggestion engine is separate from the prompt collector. History lookup runs in Zsh. An eligible directory operand can start one asynchronous nbsp dirs process for the resolved parent; that worker is bounded to 50 milliseconds, 1,024 immediate child-directory records, and 64 KiB of framed output. Moving this work off the keypress path does not make it free; it changes when and where the work runs.

Implementation choices visible in the current source include:

  • fixed-size buffers for repository discovery and direct HEAD reads;
  • a bounded cache file read followed by complete validation and an EOF check;
  • one growable output buffer for prompt rendering, while the data command writes its framed records directly to the selected output stream;
  • allocation-free NVM version extraction;
  • a bounded Git child process with a clean repository-selection environment, output cap, timeout, and complete porcelain-v2 parse before publication;
  • same-directory cache publication by fsync of the temporary file followed by atomic pathname replacement;
  • sorted, parent-keyed autosuggestion directory snapshots queried in Zsh without a filesystem syscall on each redraw.

These are implementation descriptions, not evidence of a speedup relative to an unspecified baseline.

Native safety checks

sh tests/run_memory_checks.sh requires Python 3.9 or newer and runs several different checks:

  1. The configured analyzer compiler performs path-sensitive static analysis on every production C translation unit, including generated C that embeds both Zsh sources, and analyzer findings fail the run.
  2. A debug build runs the Meson test suite under AddressSanitizer and UndefinedBehaviorSanitizer with abort-on-first-error, strict string checks, and stack-use-after-return detection.
  3. A separate native debug build runs the same selected Meson tests. On Darwin, it additionally enables malloc scribbling, guard edges, and a heap check after every allocation operation.
  4. A deterministic ASan+UBSan mutation harness runs 250,000 iterations by default. It combines valid Git/cache/path/NVM seeds with mutated and random inputs up to 16 KiB, exercises all eight prompt-quoting and rendering option masks, and reaches the Git and cache parsers, codecs, path and NVM helpers, numeric parsing, prompt rendering, and buffer growth.

Interactive Zsh coverage is controlled by the Meson zsh_tests feature option:

  • auto (the default) registers the four Expect-backed tests when expect is available and otherwise omits them;
  • enabled makes expect a required program at setup time;
  • disabled does not look for expect and omits those tests.

The non-PTY Zsh syntax, data-protocol, and option-isolation tests remain registered independently. This matches Meson’s three-state feature-option semantics.

Standalone Meson setup defaults to auto. The comprehensive run_memory_checks.sh runner instead defaults NBSP_ZSH_TESTS=enabled, so Expect is required unless the caller explicitly selects auto or disabled. When an unavailable optional suite is omitted, it is not registered; the runner does not call that a skipped test.

Leak-detection boundaries

NBSP_ASAN_LEAKS=auto|enabled|disabled controls the memory-check runner’s leak policy. In auto, the runner probes the selected ASan runtime with both a clean program and an intentional leak, and requires the leak-specific diagnostic and controlled exit status instead of inferring capability only from the operating system. The test and fuzz builds are probed separately because they can resolve different runtimes. enabled still probes and fails when support cannot be demonstrated; disabled turns leak detection off explicitly. Ambient LSAN_OPTIONS does not control these checks.

That distinction matters on macOS. Upstream Clang documents that its macOS runtime can enable leak detection, while Apple documents that Xcode’s Address Sanitizer does not detect memory leaks. The runner also leaves ASan initialization-order checking off on macOS because upstream Clang marks that option unsupported there.

On Darwin, the full native suite runs with malloc scribbling and guard edges; the native C unit binary then runs directly with per-allocation heap checks. These are corruption diagnostics, not leak detection. They do not replace LeakSanitizer, and the suite does not claim to measure process memory footprint. Passing these checks is evidence for the exercised inputs and configurations, not proof of memory safety or exhaustive parser coverage.

Architectural alternatives

The following designs are not enabled by default:

  1. Persistent Zsh coprocess: avoids starting a new foreground collector for each prompt, but adds framing, crash recovery, shell-exit cleanup, and stale process handling.
  2. Native Zsh module: avoids an external foreground process, but runs native code inside the shell and couples distribution to Zsh’s module interface.
  3. Per-shell repository memoization: can avoid some repeated discovery work, but needs explicit invalidation for changed, deleted, or replaced worktrees.
  4. Profile-guided optimization: requires a documented representative workload and retained profiles before any benefit can be claimed.

For a machine-local experiment, compiler-specific CPU tuning can be requested explicitly:

meson setup build-native --buildtype=release -Doptimization=3 \
  -Db_lto=true -Dc_args=-mcpu=native

Such a binary targets the build machine’s CPU capabilities and should not be treated as a generally distributable artifact without a separate compatibility assessment.