Skip to content

fix(bench): make builtin work dominate each builtins A/B sample - #6117

Merged
Yeachan-Heo merged 4 commits into
devfrom
rust-porting/ab-builtins-pure-loop
Sep 29, 2026
Merged

Yeachan-Heo merged 4 commits into
devfrom
rust-porting/ab-builtins-pure-loop

Conversation

@Yeachan-Heo

@Yeachan-Heo Yeachan-Heo commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

What

  • The builtins A/B loop now counts using only builtins (while / [ / $(( ))) instead of $(seq …).
  • Each builtin now runs 5000 times per sample, up from 200.
  • The runner and the adapter both default to 20 samples per block. At 20 samples, the block p95 is a real 95th percentile rather than the block maximum.

Why

The suite was still INCONCLUSIVE everywhere with equal medians, locally and on the quiet runner (run 36513103797). Two things hid the builtins' cost:

  1. $(seq 200) added noise. It spawned an external process on every sample, which put fork/exec variance into each measurement.
  2. Every Shell.run has a fixed ~90 ms floor. On this host, a : command costs about 90 ms on 01511ffd5f and on dev alike. A sample profile puts essentially all of that time in pi_shell::process::DescendantBaseline::capture → processes_in_session, a scan of the whole process session per command. This is pre-existing, not a regression. It does swamp a microsecond builtin, though.

At 5000 iterations the builtin work is about 100–230 ms per sample, so the measurement reflects the vendored builtins.

Testing

  • bun packages/natives/bench/builtins.ts --json --strict: all 8 cases measured, at 160–325 ms per sample.
  • bun test scripts/native-bench-ab.test.ts passes.

Risk classification

  • low-risk
  • regression-risk
  • high-risk

GJC verdict

gajae.pr-review-verdict.v1 needs-human sha256:d11dd9f996ab82f61e6f076070b28ce36e7fc20077911d01f035809ec1f0930b reviewer:human reviewer-id:pending evidence:bun test scripts/native-bench-ab.test.ts

  • Target branch is dev
  • Tested locally
  • Changelog fragment added under packages/<pkg>/changelog.d/ (if user-facing)
  • Verdict above matches the exact PR head, not an earlier commit
  • Risk classification above matches the actual review path taken

Two sources of noise hid the vendored builtins' cost:
- `$(seq 200)` in the loop spawned an external process every sample; the
  loop now counts with `while`/`[`/`$(( ))` only.
- Every Shell.run pays a fixed ~90ms (on this host) for the descendant
  baseline scan of the whole process session, on 01511ff and dev alike
  (sampled: all time in DescendantBaseline::capture -> processes_in_session).
  An empty `:` costs 90ms on both sides. Looping 10000 times makes the
  builtin work 200-450ms per sample.

Confidence: high
Scope-risk: narrow
Tested: builtins.ts --json --strict (all 8 measured, 289-552ms per sample); native-bench-ab.test.ts
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-29T05:32:37.518997Z d61c1fe New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e19a9a1502

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/natives/bench/builtins.ts Outdated

try {
await runAbSuite("builtins", cases, 10);
await runAbSuite("builtins", cases, 5);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor the reduced sample count in the A/B runner

When the builtins suite is launched through scripts/native-bench-ab.ts without an explicit iteration override—the normal path in native-bench-ab.yml—the runner still defaults to 200 and always forwards --iterations 200, so this new default of 5 is never used. With each sample now taking roughly 289–552 ms, one 15-block A/B round performs 30 adapter invocations of 8 × 203 calls and cannot fit within the workflow's 120-minute limit (and individual adapters may hit the runner's 10-minute timeout). The suite-specific reduced count therefore needs to be honored by the runner, rather than only by direct adapter execution.

Useful? React with 👍 / 👎.

@probepark probepark left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review (head e19a9a1, gajae-reviewer on behalf of probepark)

CI: gate pending: everything that ran passed except Merge approval bootstrap, which waits on the needs-human verdict. Affected path validation / install-methods is still pending.
Scope: +10 / -6, 1 file, packages/natives/bench/builtins.ts (bench adapter only).
Conventions: no changelog fragment (bench-only change, not user-facing). No generated files. No labels. The diff digest matches the PR body (865ff97b…).
Notable:

  • packages/natives/bench/builtins.ts:106: runAbSuite("builtins", cases, 5) sets only the adapter's own default. scripts/native-bench-ab.ts:10 sets DEFAULT_ITERATIONS = 200, and runBenchAdapter (scripts/native-bench-ab.ts:316-325) always forwards --iterations <n>. .github/workflows/native-bench-ab.yml:112 takes that path whenever the iterations input is empty. So the default of 5 never applies in the A/B run. (The Codex P1 comment reports the same thing; I confirmed it at this head.)
  • packages/natives/bench/builtins.ts:22 sets LOOP = 10000. By the PR's own numbers (289–552 ms per sample), one adapter invocation on the runner path does 8 cases × (3 warmup + 200) samples, roughly 470–900 s. runJsonCommand kills the adapter after 10 * 60_000 ms (scripts/native-bench-ab.ts:303), and the runner reports that as AdapterError. Even when an invocation fits, one 15-block round is 30 invocations, which is several hours against timeout-minutes: 120 (native-bench-ab.yml:29). The INCONCLUSIVE retry doubles the block count, making it worse. Before this change a sample was about 90 ms plus 200 builtins, so the suite fit easily. This change makes the default builtins A/B dispatch unrunnable.
  • Checked and clean: the while [ "$__builtins_ab_i" -lt 10000 ] counter uses only builtins and does not collide with the case bodies (B08 uses n/sum). expectedStdout.repeat(LOOP) still matches, and timeoutMs: 30_000 per Shell.run leaves headroom over the ~0.5 s samples.

Blocking: item 1 and item 2 (one defect). The reduced sample count has to reach the runner. Options: a per-suite iterations in SUITES that runBenchAdapter uses when no --iterations is given, or omitting --iterations unless the user passed it. Either way, the default builtins dispatch should fit under the 10-minute adapter timeout and the 120-minute job.

Body verdict line is owned by pending; not edited. Suggested verdict line: gajae.pr-review-verdict.v1 needs-human sha256:865ff97bf0974cf41349083b4425fe642fc2e59ff12b9a742800e895356cb5df reviewer:critic reviewer-id:gajae-reviewer evidence:runner-forces-iterations-200;loop10000-exceeds-adapter-10min-timeout;digest-verified

Verdict: gajae.pr-review-verdict.v1 needs-human sha256:865ff97bf0974cf41349083b4425fe642fc2e59ff12b9a742800e895356cb5df reviewer:critic reviewer-id:gajae-reviewer evidence:runner-forces-iterations-200;loop10000-exceeds-adapter-10min-timeout;digest-verified

… runner

probepark (#6117 at e19a9a1): the adapter's default of 5 never applied
because native-bench-ab.ts always forwards --iterations (DEFAULT_ITERATIONS
200). With the 10,000-iteration loop that is ~8-15 min per adapter call,
over the 10-minute adapter timeout and the 120-minute job. Add a per-suite
defaultIterations (builtins: 5) used when --iterations is not given; an
explicit value still wins. ~35 s per invocation, ~18 min per 15-block round.
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Fixed the blocker at e19a9a1 in c06dd9b. The adapter's own default of 5 was never reaching the runner, so scripts/native-bench-ab.ts now takes a per-suite defaultIterations, set to 5 for builtins. parseNativeBenchOptions uses it whenever --iterations isn't passed, and an explicit value still wins. The workflow already omits --iterations when its input is empty, so the default dispatch now picks up 5.
Budget: 8 cases × (3 warmup + 5) samples × ~0.3–0.55 s comes to ~20–35 s per adapter invocation, well under the 10 min adapter timeout. A 15-block round is 30 invocations, about 10–18 min, and even with the INCONCLUSIVE doubling it stays inside the 120 min job.
New test: builtins defaults to 5, an explicit --iterations 50 wins, grep keeps 200, and 0 is rejected. It fails without the runner change. native-bench-ab.test.ts passes 13/0. Body digest refreshed to this head. @probepark please re-review c06dd9b.
—
[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c06dd9b6b1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/native-bench-ab.ts Outdated
builtins: { adapter: "packages/natives/bench/builtins.ts", actualSuite: "builtins", cases: ["B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08"] },
// Each builtins sample loops the builtin 10,000 times (~0.3-0.55 s), so 200
// samples per case would blow the 10-minute adapter timeout and the job budget.
builtins: { adapter: "packages/natives/bench/builtins.ts", actualSuite: "builtins", cases: ["B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08"], defaultIterations: 5 },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve enough samples for the p95 gate

With the normal builtins invocation, this reduces every block to five timings, while summarizeBlock() still computes quantile(samples, 0.95). For five values that is always the block maximum; across blocks, the median of those maxima corresponds to roughly the 87th percentile, so a regression confined to the slowest 5–10% of samples will usually be absent from most blocks and can pass the advertised p95 gate. Keep enough per-block observations to estimate p95, or change the aggregation/gate explicitly rather than applying the five-sample default.

Useful? React with 👍 / 👎.

snowykr
snowykr previously approved these changes Sep 29, 2026

@snowykr snowykr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict

APPROVED

Summary

This PR replaces the builtins benchmark's external seq iteration with an in-shell loop, increases repetitions to 10,000, and sets a builtins-specific default of five samples per block. The changed default is scoped to this suite and explicit --iterations still overrides it. I found no verified merge-blocking defects at reviewed head c06dd9b6b10f5867e1e450707b6d31405e4765f5.

Findings / Required Changes

No blocking or actionable findings.

Non-blocking Observations

  • The loop controller calls [ once per iteration, including for B03, which itself benchmarks test and [. Because the complete command is timed, B03 includes controller work; differences in the target builtin can therefore influence both the fixture and loop-control time. This is limited to B03's attribution and does not invalidate the entire suite. Consider isolating that controller cost while preserving supported-shell compatibility.
  • With five samples per block, the block p95 is the maximum observation and feeds the latency verdict. That is a lower-resolution, more maximum-sensitive estimate than the former 200-sample default; no applicable minimum sample-count contract was identified, so this is a methodology consideration rather than a merge blocker.

CI / Verification

At the reviewed head, the affected-path test for scripts/native-bench-ab.test.ts and the affected-path/plan validation checks succeeded. The check-run set contained 25 successes and four skipped checks; PR contract bootstrap failed because authorized merge-verdict evidence was unavailable, not because benchmark code or tests failed. The native benchmark workflow is manual and no result artifact bound to this head was present, so I could not independently validate the reported benchmark measurements.

Axis Coverage

Axis Verdict Coverage
A1 — Intent / Policy / Contract APPROVED Suite-specific default and explicit override agree with runner behavior; no contract requiring 200 samples for every suite.
A2 — Architecture / Correctness / Failure APPROVED No verified runtime/failure defect; five-sample p95 sensitivity is noted above as non-blocking.
A3 — Security / Privacy / Trust APPROVED No new trust boundary, attacker-controlled execution path, or sensitive-data flow identified.
A4 — Verification / Tests / CI APPROVED Added default/override test passes in affected-path CI; adapter behavior has no retained head-bound benchmark artifact.
A5 — Context / Compatibility / Platform APPROVED Runner and macOS workflow honor the per-suite default; B03 loop-control overlap is a non-blocking attribution concern.

Limitations

The benchmark itself was not independently run during this review; measurement-related conclusions are based on the exact-head/base source and CI metadata.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

@probepark snowykr has approved the current head c06dd9b, and CI is green apart from the approval gate. The only thing blocking merge is your CHANGES_REQUESTED on e19a9a1. Its blocker (the runner forcing --iterations 200) is fixed at this head. Could you re-review c06dd9b?
—
[repo owner's gaebal-gajae (clawdbot) 🦞]

Yeachan-Heo added 2 commits September 29, 2026 14:29
Review of #6117: with 5 samples per block, quantile(samples, 0.95) is the
block maximum, so the p95 gate would miss a regression confined to the
slowest samples. The builtins suite now defaults to 20 samples per block
and halves the in-command loop to 5000 iterations so one A/B round keeps
about the same wall time; builtin work (~100-230ms) still dominates the
~90ms Shell.run floor. The SUITES doc states the >=20 floor.

Confidence: high
Scope-risk: narrow
Tested: native-bench-ab.test.ts (13 pass); builtins.ts --json --strict (all 8 measured, 160-325ms per sample)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d61c1fea5d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

builtins: { adapter: "packages/natives/bench/builtins.ts", actualSuite: "builtins", cases: ["B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08"] },
// Each builtins sample loops the builtin 10,000 times (~0.3-0.55 s), so 200
// samples per case would blow the 10-minute adapter timeout and the job budget.
builtins: { adapter: "packages/natives/bench/builtins.ts", actualSuite: "builtins", cases: ["B01", "B02", "B03", "B04", "B05", "B06", "B07", "B08"], defaultIterations: 20 },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bound builtins reruns to the workflow budget

When a builtins comparison remains INCONCLUSIVE through the first two rounds, the retry loop at scripts/native-bench-ab.ts:648-655 runs 15 + 30 + 60 blocks, or 210 base/head adapter invocations. With this default, each invocation performs 8 × (3 warmups + 20 samples); at the commit's reported 160–325 ms per sample, the three rounds require roughly 103–209 minutes before setup/build overhead, while .github/workflows/native-bench-ab.yml:29 kills the job after 120 minutes. Thus the third-round path can never produce its intended verdict for normal measured timings; use a suite-specific block/retry budget or extend the workflow deadline.

Useful? React with 👍 / 👎.

@Yeachan-Heo
Yeachan-Heo requested a review from snowykr September 29, 2026 05:51

@probepark probepark left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review (head d61c1fe, gajae-reviewer on behalf of probepark)

CI: green — 29 pass, 6 skipped (opt-in/Windows lanes), only Merge approval bootstrap failing (gate waiting on the verdict). Approve gate ALLOW; plan covered check:@gajae-code/natives.
Scope: +33 / -9, 3 files — packages/natives/bench (builtins adapter), scripts/native-bench-ab.ts + its test
Conventions: CHANGELOG none (bench tooling only), generated files none, labels none
Notable:

  • Delta since the last reviewed head c06dd9b: 0d5f358 (LOOP 10000→5000, builtins defaultIterations 5→20 in both scripts/native-bench-ab.ts:86 and packages/natives/bench/builtins.ts:106, test expectation updated) plus a merge of origin/dev (#6042 files only; they are not in the base...head diff). The earlier blocker from e19a9a1 (suite default never reaching the runner) stays fixed: parseNativeBenchOptions resolves iterations ??= SUITES[suite].defaultIterations ?? DEFAULT_ITERATIONS (scripts/native-bench-ab.ts:131) before the positive-integer check, and runBenchAdapter passes --iterations explicitly (:336). Budget: 8 cases x 20 samples x ~0.2-0.32 s ≈ 30-50 s per adapter call, well inside the 10-minute runJsonCommand timeout (:315).
  • scripts/native-bench-ab.ts:84 — the comment still says "loops the builtin 10,000 times (~0.3-0.55 s)", but LOOP is now 5000 (~100-230 ms of builtin work per builtins.ts:17). Stale comment only; non-blocking.
    Blocking: none

Body verdict line is owned by pending; not edited. Suggested verdict line: gajae.pr-review-verdict.v1 merge-approved sha256:d11dd9f996ab82f61e6f076070b28ce36e7fc20077911d01f035809ec1f0930b reviewer:human reviewer-id:probepark evidence:ci-green;approve-gate-allow;suite-default-iterations-reach-runner;adapter-budget-checked

Verdict: gajae.pr-review-verdict.v1 merge-approved sha256:d11dd9f996ab82f61e6f076070b28ce36e7fc20077911d01f035809ec1f0930b reviewer:human reviewer-id:probepark evidence:ci-green;approve-gate-allow;suite-default-iterations-reach-runner;adapter-budget-checked

@Yeachan-Heo
Yeachan-Heo merged commit 35f2808 into dev Sep 29, 2026
35 of 36 checks passed
Yeachan-Heo pushed a commit that referenced this pull request Sep 29, 2026
A-PI-BUILTINS-BASE, C-PTY, C-POWER, C-APPEARANCE, C-PROF, B-CRASH-HANDLER
and C-ISO were held in-progress because their listed benchmarks did not
reach their native code. The surface suites from #6062 (with #6069, #6089
and #6117 fixing what they found) now PASS against 01511ff: pty, power,
appearance, prof, crash, iso and builtins, the last with byte-identical
stdout for eight builtins as the per-builtin parity evidence. A
tag-build-verify rehearsal on dev 35f2808 (run 36538316951) is green.

Confidence: high
Scope-risk: narrow
Tested: verify-rust-porting-inventory (schema valid, adopted-row checks); verify-rust-porting-inventory.test.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants