Skip to content

perf(assert): remove per-assertion subshell forks#781

Merged
Chemaclass merged 7 commits into
mainfrom
perf/772-remove-per-assertion-forks
Jul 12, 2026
Merged

perf(assert): remove per-assertion subshell forks#781
Chemaclass merged 7 commits into
mainfrom
perf/772-remove-per-assertion-forks

Conversation

@Chemaclass

Copy link
Copy Markdown
Member

🤔 Background

Related #772 (part of #761)

The assertion layer still forked avoidable subshells on its hottest paths on Bash 3.2 (macOS): assert_equals/assert_not_equals captured strip_ansi twice per call, failure-path label resolution nested two-to-three $(...) captures, and assert_true/assert_false forked command -v | grep just to detect an alias.

💡 Changes

  • assert_equals/assert_not_equals strip ANSI through a return slot (_BASHUNIT_STR_STRIPPED_OUT), removing two forks each on the success path.
  • Failure-path labels (assert::label, fail, handle_bool_assertion_failure) resolve via fork-free return slots; echoing helpers kept as thin wrappers for other callers.
  • Alias detection now uses the alias builtin instead of command -v | grep (verified equivalent on bash 3.2.57).
  • 100×10 assert_equals: ~1.50s → ~0.76s. Console output byte-identical; regression tests cover each new slot helper against its own internal local names.

Add bashunit::str::strip_ansi_to_slot writing _BASHUNIT_STR_STRIPPED_OUT
with a zero-fork plain-text fast path; assert_equals and assert_not_equals
now read the slot instead of paying a command-substitution fork twice per
call. strip_ansi kept as a thin echoing wrapper for other callers.

100x10 assert_equals: ~1.50s -> ~0.72s on macOS bash 3.2, Apple Silicon.

Part of #761.
Add return-slot variants find_test_function_name_to_slot,
normalize_test_function_name_to_slot and bashunit::assert::label_to_slot
(slots _BASHUNIT_HELPER_TESTFN_OUT / _BASHUNIT_HELPER_NORMALIZED_OUT /
_BASHUNIT_ASSERT_LABEL_OUT). fail, handle_bool_assertion_failure and every
label call site in assert.sh now write the slot instead of capturing with
$(...). The find slot is an independent copy (not a wrapper) to preserve the
FUNCNAME fallback depth; the echoing helpers stay as thin wrappers for the
other assert_*.sh callers.

Regression tests cover each slot helper called with one of its own internal
local names (dynamic-scope trap, bash-style.md).

Part of #761.
… grep

run_command_or_eval replaces the per-call `command -v "$cmd" | grep -cE
'^alias'` pipeline with `alias -- "$cmd"`, a fork-free builtin that exits
0 only for a defined alias. Equivalent for functions, binaries and unknown
commands (all non-zero). Verified on bash 3.2.57.

Regression tests cover the alias (zero and non-zero exit) and function paths.

Part of #761.
@Chemaclass Chemaclass added the enhancement New feature or request label Jul 12, 2026
@Chemaclass Chemaclass self-assigned this Jul 12, 2026
Chemaclass and others added 3 commits July 12, 2026 21:35
Self-review follow-ups:
- assert_within_delta resolved its label eagerly on every call; move it into
  the two failure branches so the success path skips the FUNCNAME walk and
  normalize entirely (matches every other assertion).
- bashunit::str::rpad now strips ANSI via the return slot instead of a
  $(strip_ansi) fork per formatted output line.

No behaviour change; console output byte-identical (snapshots green).

Part of #761.
… input

run_command_or_eval's fork-free alias check used `alias -- "$cmd"`, but for a
"name=value" argument that *defines* an alias and returns 0, wrongly taking the
eval branch (and mutating the alias table). Route any cmd containing "=" or
whitespace straight to direct execution, matching the previous
`command -v | grep` behaviour. Regression tests cover both cases.

Part of #761.
@Chemaclass Chemaclass enabled auto-merge (squash) July 12, 2026 19:50
@Chemaclass Chemaclass merged commit 3797445 into main Jul 12, 2026
31 checks passed
@Chemaclass Chemaclass deleted the perf/772-remove-per-assertion-forks branch July 12, 2026 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants