Skip to content

test(aot): share one build and run the tests of entry-point-names.test.ts concurrently - #47463

Open
robobun wants to merge 3 commits into
mainfrom
robobun/a6edda21/faster-entry-point-names-test
Open

robobun wants to merge 3 commits into
mainfrom
robobun/a6edda21/faster-entry-point-names-test

Conversation

@robobun

@robobun robobun commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Behaviour change: none

Problem

  • test/aot/entry-point-names.test.ts takes 68 s on the darwin x64 lane (build #135385). The other lanes take 3 to 23 s.
  • Its 11 tests run in series. They start 14 builds and 8 executables.
  • Two tests in each mode build the same entry points.

Fix

  • Those two tests share one build in each mode. The file starts 12 builds.
  • All 11 tests run concurrently. Each test has its own folders.
  • Each run compares stdout, stderr, the exit code and the signal with exact values. Each build that must fail compares the message on stderr, exit code 1, and that it left no executable.
  • Verified: bun bd test test/aot/entry-point-names.test.ts takes 144 s before and 48 s after (medians of 5 and 8 runs). A release build takes 4.4 s before and 1.2 s after. The Notes have the CI lanes.

Background

  • Each test builds an executable with bun build --compile, with and without --aot, and starts it. A build writes a copy of Bun.
  • An ASAN build of Bun runs 5 concurrent tests at a time. Other builds run 20.
  • setDefaultTimeout(240_000) stays. It equals the limit that spawned() gives a command under ASAN. The default of the harness reaches only the first file of a process.
  • Considered beforeAll for the shared build: it delays the other tests. Considered fewer executables: each of the 8 starts at another first entry point.
Notes

Timings on one machine (Linux x64, 12 threads). The after column is the second commit. The head differs from it only by the slots, which do not bind on 12 threads. On 4 cores the head takes 1.34 s to 1.73 s (50 runs).

Bun before after
bun bd (debug, ASAN) 114.1 s to 147.2 s, median 144.0 s (5 runs) 38.7 s to 71.1 s, median 48.3 s (8 runs)
release (canary da867c60b), 12 threads, 10 runs 3.84 s to 4.79 s, median 4.38 s 0.98 s to 1.45 s, median 1.15 s
release, 4 cores (taskset -c 0-3), 10 runs 3.61 s to 3.93 s, median 3.76 s 1.69 s to 1.82 s, median 1.71 s
release, 2 cores (taskset -c 0-1), 10 runs 4.47 s to 5.33 s, median 4.66 s 2.61 s to 2.87 s, median 2.76 s
  • In the debug build each build writes an executable of 1.6 GB, and a build with --aot takes 10 to 24 s. The disk and the five --aot builds set the time, and the runs vary with the state of the disk. Two pairs that ran one after the other: 147.2 s then 44.7 s, 144.0 s then 38.7 s.
  • 30 runs in a row pass with the release build.
  • The file also passes beside other files in one process: bun test --parallel=2 --max-concurrency=2 with extensionless-entry-points.test.ts and exit-hooks-of-an-executable.test.ts.
  • Memory, release build, Linux: MemAvailable drops by about 0.55 GB at the peak of the concurrent file. It dropped by 0.17 GB before.

Timings in CI

lane main (build #135385) first commit (build #135747) with a slot for each command (build #135759)
darwin x64 68 s did not run 11.8 s
windows 2019 x64 23 s 5.3 s 6.1 s
debian 13 x64-asan 17 s 6.7 s 7.4 s
alpine 3.23 x64 10 s 2.3 s 2.4 s
windows 11 aarch64 10 s 3.7 s 5.1 s
ubuntu 25.04 x64 9 s 3.2 s 6.3 s
debian 13 x64 8 s 5.5 s 4.0 s
ubuntu 25.04 aarch64 6 s 2.5 s 5.8 s
alpine 3.23 aarch64 6 s 1.5 s 1.5 s
debian 13 aarch64 6 s 3.0 s 6.6 s
darwin aarch64 3 s (a macOS 15 agent) 10.6 s (a macOS 26 agent) 2.0 s (a macOS 15 agent)
  • The main column is not like for like with the others. On main the file ran beside other files. A changed test file runs alone and first in its shard. Each cell is one run.
  • The head of this PR is the first commit plus the DisposableStack. Build #135810 ran it, and no lane reports a failure of this file. I read its time on four lanes: the three in the next section, and darwin x64. For the other lanes I know only that the file is not in the list of failures.
  • darwin x64: 11.8 s with the second commit and 11.4 s with the head (build #135810), on the same agent. Both are the file alone. The 68 s on main is the file beside three other files that build executables. No run of this PR has the file in that group, so the time there after a merge is not known.
  • On the darwin x64 agent the tests finish one at a time, 1.05 to 1.84 s apart. The first start of a new executable costs that much there and the starts do not overlap. bundler_compile.test.ts finishes one test every 1.85 s on that lane. This file starts 8 new executables. Concurrency lets the builds overlap and puts the 8 starts in the queue together. It does not remove a start.
  • The macOS 26 agent of darwin aarch64 behaves like the darwin x64 agents. Files that this PR does not touch are slower there when they start new executables: exit-hooks-of-an-executable.test.ts takes 5.4 s there and 0.30 s on a macOS 15 agent.
  • In builds #135759 and #135810 on darwin x64, the same five tests of test/aot/in-place-executables.test.ts timed out at 120 s, and the file passed on the retry. This PR does not touch that file. Main builds #135281, #135376 and #135526 also list it as failed once and passed on a retry on darwin x64, so it is not from this PR. I did not read which of its tests failed on main. It has the same pattern as this file (concurrent tests, new executables) and the 120 s limit of the harness. This file has a limit of 240 s.

A slot for each command was tried and removed

  • The second commit put each command behind slot() of the harness, after a review note about a debug build on few processors. In that case it helps: on 2 cores with a debug build (with ASAN, and 20 concurrent tests forced) the longest command takes 49 s with the slots. Without them the tests with an --aot build took 112 to 122 s each, and spawned() ends a command at 90 s where Bun has no sanitizer.
  • The third commit removes the slots. The protection for a debug build on few processors is gone with them.
  • The file on the three lanes that moved most, same shard in each build, one run each:
lane first commit, no slots (#135747) slots (#135759) head, no slots (#135810)
ubuntu 25.04 x64 3.2 s 6.3 s 4.2 s
ubuntu 25.04 aarch64 2.5 s 5.8 s 3.1 s
debian 13 aarch64 3.0 s 6.6 s 4.7 s
  • What this shows: the file got slower on all three lanes when the slots went in, and faster on all three when they came out. What it does not show: the size of the effect. I expected the head to return to about 3 s. One lane did. Two did not (4.2 s and 4.7 s).
  • The two builds without slots differ by 0.6 to 1.7 s on the same lane, so the run-to-run spread is as large as much of the effect. The short files beside this one on the same shards also vary between builds (test/cli/bun.test.ts: 118 to 230 ms on ubuntu x64, 178 to 441 ms on debian aarch64). The only code difference between those two builds is the DisposableStack. I have no sign that it costs time (the disposal takes 0.02 s in a local trace), and these runs cannot rule it out.
  • debian x64 went the other way with the slots (5.5 s to 4.0 s), and the Alpine lanes did not move.
  • A local trace shows how the slots can cost time. A machine with four processors has four slots. The file has five --aot builds. The fifth starts when the first ends, and each run of an executable waits behind the four builds. On one Linux machine I could not measure a difference on 4 cores: both variants take 1.4 to 1.7 s there.

The shared folders

  • The folders of the two shared builds are in a DisposableStack. dispose() removes each folder, also when the removal of another one throws, and then throws that error.

The time limit

  • spawned() ends a command at 90 s, and at 240 s under ASAN or with AOT_BUILD_FLAGS. A test limit below that would end the test before spawned() ends the build and says what the build was doing.
  • The harness sets 120 s when it is first imported. bun:test drops that value at the next file of the same process, and the harness is not evaluated again. Shown with bun test --timeout=300 test/aot/entry-point-names.test.ts test/aot/exit-hooks-of-an-executable.test.ts: the second file has no limit of its own, and its test times out after 300 ms.

Assertions

  • A run of an executable (8 places). Before: stdout.trim() and the exit code. After: the exact stdout, stderr: "", exitCode: 0, signal: null, in one toEqual on what spawned() returns.
  • No output files to bundle. Before: an exit code that is not 0, then the text in stdout or stderr. After: the text on stderr, exit code 1, and no file none* in the folder.
  • Multiple files share the same output path. Before: an exit code that is not 0, then the text on stderr. After: the text on stderr followed by the shared output sub/typed.js, exit code 1, and no file equal* in the folder.
  • expect() calls go from 28 to 26. The two that are gone are the built() checks of the two builds that no longer run.
  • The test of the listed names uses lastIndexOf in place of includes. The answer is the same. The names are about 55 KB before the end of a file of 71 to 140 MB. In the debug build each includes read 1.6 GB and took about 3 s on the thread of the test runner.

Also run

  • test/aot/test-files-self-skip.test.ts and test/aot/every-test-runs-in-ci.test.ts pass.
  • prettier --check, oxlint and tsc report nothing for the file.

no test proof · iteration 2 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/aot/entry-point-names.test.ts

… concurrently

The two tests that ask the executable of src/main.ts and its five other
entry points share one build in each mode. The file starts 12 builds
where it started 14.

All 11 tests run concurrently. Each has folders of its own, and the two
that share an executable only read it.

Each run of an executable compares stdout, stderr, the exit code and the
signal with exact values. Each build that must fail compares the message
on stderr, exit code 1, and that it wrote no executable. The duplicate
output error must name the shared output, sub/typed.js.
@robobun

robobun commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status

  • How the time was measured: bun bd test test/aot/entry-point-names.test.ts on Linux x64 (debug build with ASAN). It takes 144 s before and 48 s after (medians of 5 and 8 runs). A release build (canary da867c60b) takes 4.4 s before and 1.2 s after.
  • CI, Linux and Windows lanes: 1.5 s to 6.7 s with the first commit (build #135747). They took 6 s to 23 s on main (build #135385).
  • CI, darwin x64: 11.4 s on the head (build #135810) and 11.8 s on the second commit (build #135759), same agent, with the file alone and first in its shard. The 68 s on main is the file beside three other files that build executables. These numbers are not like for like. No run has this PR's file in that group.
  • The second commit put each command behind a slot, and the third commit removes the slots again. On three lanes the file took 2.5 to 3.2 s, then 5.8 to 6.6 s with the slots, then 3.1 to 4.7 s on the head. The slots cost time there. How much is not clear from one run for each lane.

CI on the head (852c22d, build #135810, finished)

  • No lane reports a failure of test/aot/entry-point-names.test.ts. I read its result on four lanes (ubuntu x64, ubuntu aarch64, debian aarch64, darwin x64): 11 pass, 0 fail.
  • The red test is test/aot/engine/ir-6.test.ts, on five lanes. This PR does not touch it. The same failure is on main build #135526, the commit this branch starts from.
  • The other failures passed on a retry. One of them, test/aot/in-place-executables.test.ts on darwin x64, is also listed as failed once on main builds #135281, #135376 and #135526.

Open, for a maintainer

  • Review note, not addressed: without the slots, a debug build without a sanitizer on few processors can reach the 90 s limit of spawned() with this file. A gate for that build alone is possible. I did not push one because I cannot run such a build.
  • The time of this file on darwin x64 beside other files is not measured.
  • The file keeps setDefaultTimeout(240_000), which was there before. test/CLAUDE.md says not to set a timeout. The reasons to keep it are in the PR body.

@robobun

robobun commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 10:00 AM PT - Oct 30th, 2026

❌ @robobun, your commit 852c22d has 1 failures in Build #135810 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 47463

That installs a local version of the PR into your bun-47463 executable, so you can run:

bun-47463 --bun

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 1c05c67 and 852c22d.

Files selected for processing: 1
  • test/aot/entry-point-names.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

Walkthrough

The AOT entry-point name tests share build promises and a temporary directory. They compare complete process results and check build errors and the absence of output files after failures.

Changes

AOT entry-point tests

Layer / File(s) Summary
Shared executable test setup
test/aot/entry-point-names.test.ts
The tests run concurrently, share build promises and a temporary directory, and clean up the directory after the suite.
Process and build failure assertions
test/aot/entry-point-names.test.ts
The tests check complete process results, including trailing newlines, stderr, exit codes, and signals. Build-failure checks match specific errors and verify that no matching output file was created.

Possibly related PRs

  • oven-sh/bun#47468: Both PRs share AOT builds across related tests and run test cases concurrently.
  • oven-sh/bun#47482: Both PRs use concurrent AOT test execution and compare complete process results.
  • oven-sh/bun#47474: Both PRs share AOT builds and compare complete process results.

Suggested reviewers: jarred-sumner

Priority: Low

Merge Risk: Minimal · up to 852c2

The shared builds and concurrent tests have no established blocking defect. The change is mergeable subject to normal checks.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main test optimization: sharing one build and running the entry-point tests concurrently.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description provides a detailed explanation of the problem, fix, behavior, performance impact, testing method, CI evidence, and validation results. It does not use the template headings exactly, b…
Full details: Description check

Explanation

The description provides a detailed explanation of the problem, fix, behavior, performance impact, testing method, CI evidence, and validation results. It does not use the template headings exactly, but it includes the required information.

Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Beyond the inline findings, I also checked that the shared build's folder outlives both consumers (it is only disposed in afterAll, and the two tests that use it just spawn or read the file), that spawned() returns exactly the four keys { stdout, stderr, exitCode, signal } so the new toEqual shapes are exact rather than vacuous, and that the rebuilt expected string matches the old value plus a trailing newline.

Extended reasoning...

The change is confined to test/aot/entry-point-names.test.ts: it switches the file to test.concurrent, memoizes one build per mode into a module-level shared tempDir disposed in afterAll, and tightens every run assertion to the full spawned() result. It touches no security-sensitive surface. The two inline findings (unbounded concurrent builds with no slot gating, and afterAll disposing shared folders without try/finally) are what keep this from an approve; the ruled-out checks above are recorded so a human reviewer need not repeat them.

Comment on lines +123 to +127
function withEveryEntryPoint(): Promise<string> {
if (!once) {
const dir = tempDir("aot-entry-point-names", files);
shared.push(dir);
once = build(String(dir), [...flags], ["src/main.ts", ...additional], title);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 (optional) Developers running this file on a debug build with few cores get spurious failures: a --aot build killed at 90 s. The file now starts up to 12 builds at once (test/aot/entry-point-names.test.ts:127 and every build() call) with no slot() bound, unlike in-place-executables.test.ts:15-20 which gates each command on a processor. spawned() kills a child at limitOfARun = 90 s (test/aot/harness/commands.ts:58) when not ASAN. Fix: wrap each build() in slot() from ./harness/commands so concurrent builds never exceed availableParallelism, matching the sibling file.

Why this was flagged

Trigger: bun bd test test/aot/entry-point-names.test.ts on a debug, non-ASAN Bun with 2-4 cores, or any lane where the runner's --max-concurrency cap (scripts/runner.node.ts:1425-1427) does not apply because the file is run directly. bun test's default max-concurrency is 20, so all 11 tests start together and test/aot/entry-point-names.test.ts:107 spawns a build for each; the PR says each --aot debug build alone takes 10 to 24 s. With 5 to 6 --aot builds sharing 2 to 4 cores, one build's wall clock exceeds limitOfARun = 90 s (test/aot/harness/commands.ts:58, chosen at commands.ts:73-79 since isASAN is false and moreBuildFlags is empty); Bun.spawn kills it with SIGKILL (commands.ts:89) and built() at commands.ts:170-175 fails on exitCode. On the base branch the tests ran in series, one build at a time, so no build competed for cores. The author measured only release builds on 2 cores (2.9 s each) and ASAN debug on 12 threads, which never exercise the 90 s non-ASAN limit under contention. Remedy: gate every build() through slot() as in-place-executables.test.ts does.

Verification: Trigger: a direct run on a debug Bun without ASAN on a machine with few cores. test/aot/entry-point-names.test.ts:15 makes 11 tests start together. Each build calls spawned(...) at line 107 with no slot() wrapper. test/aot/harness/commands.ts:58 limitOfARun = 90_000; built() at commands.ts:170-175 fails on the nonzero exit code. The base ran these builds serially, so this risk is new.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done in 1c05c67. Each command now goes through slot(), as in in-place-executables.test.ts.

I checked the case with a debug build on 2 cores and 20 concurrent tests. Without the slots, the tests with an --aot build took 112 to 122 s each, nearly all of it in the build. With the slots, the longest command takes 49 s, and the file takes 113 s where it took 128 s.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Correction to my reply above: I removed the slots again in 852c22d. This note is not addressed.

The slots help in the case you describe (2 cores, debug build: longest command 49 s, against 112 to 122 s without them). In CI they cost time. On ubuntu x64, ubuntu aarch64 and debian aarch64 the file went from 2.5 to 3.2 s (build 135747) to 5.8 to 6.6 s (build 135759), while the eight files after it on the same shards stayed within about 5%. A machine with four processors has four slots and the file has five --aot builds, so the fifth starts when the first ends, and each run of an executable waits behind the four builds.

That is one run for each lane, and debian x64 went the other way (5.5 s to 4.0 s). The next CI run shows whether the three lanes return to about 3 s.

So a debug build without a sanitizer on few processors can still reach the 90 s limit of spawned() with this file.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The CI run I pointed to is in: 4.2 s, 3.1 s and 4.7 s on the three lanes without the slots (they were 6.3 s, 5.8 s and 6.6 s with them, and 3.2 s, 2.5 s and 3.0 s in the first build). The lanes came down, but two did not return to about 3 s. The details are in my reply on the newer thread at line 15.

Comment thread test/aot/entry-point-names.test.ts Resolved
…cessors, and dispose of each shared folder

Each command waits for a slot of the harness, so no more commands run at
once than the machine has processors. With a debug build on 2 cores the
longest command takes 49 s. Without the slots the builds with --aot took
about 115 s each, and spawned() ends a command at 90 s where Bun has no
sanitizer.

The folders of the two shared builds are held in a DisposableStack. When
one of them cannot be removed, the other is still removed.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM — the second commit addresses both earlier inline notes (commands now go through slot(), shared folders live on a DisposableStack), and the rest is a test-only tightening.

What was reviewed:

  • slot() wrapping: withEveryEntryPoint() is awaited before the outer spawned, so no slot is held while waiting for another — no deadlock against availableParallelism().
  • The sub[\\/]typed\.js regex against both producers: src/bundler/aot/options.rs:567 emits ...output path: sub/typed.js and generateChunksInParallel.rs:444-453 emits the path on its own sub/typed.js: line, so both [] and --aot match.
  • The readdirSync "nothing left behind" checks: neither fixture dir has a file starting with none/equal, so they fail only if a build wrote one.
  • DisposableStack.dispose() runs every registered tempDir dispose and aggregates throws, so a Windows EBUSY on one folder no longer skips the other.
Extended reasoning...

The diff touches a single test file, test/aot/entry-point-names.test.ts, with no production code and no security-sensitive surface. It switches the file to test.concurrent, gates every spawned command on a processor slot via the existing slot() helper from test/aot/harness/commands.ts, shares one build between two tests per variant, and tightens assertions to whole {stdout, stderr, exitCode, signal} objects — the same shape the sibling in-place-executables.test.ts already uses. The two concerns raised on the first push were both fixed in commit 1c05c67, the path is not covered by CODEOWNERS, and the hunt ran dry, which decided approve.

…t a slot

The file has five builds with --aot and a slot for each processor. On a
machine with four processors the fifth build starts when the first one
ends, and each run of an executable waits behind the four builds.

With the slots the file took 5.8 s to 6.6 s on three CI lanes with four
processors. Without them it took 2.5 s to 3.2 s there. The files beside
it took the same time in both builds.

The folders of the shared builds stay in the DisposableStack.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Beyond the inline finding, I also checked the shared-build plumbing: withEveryEntryPoint() assigns once synchronously before any await, so the two concurrent consumers cannot start two builds, and a failed build rejects both tests rather than hanging; shared.dispose() in afterAll now disposes the second folder even when the first rmSync throws; and spawned() returns exactly the four keys (stdout, stderr, exitCode, signal) that the new toEqual calls compare, so the stricter assertions are not vacuous or over-constrained. lastIndexOf(...) !== -1 is equivalent to includes for the listed-names check.

Extended reasoning...

The change is confined to one test file, test/aot/entry-point-names.test.ts, with no production code and no security-sensitive surface. It makes all tests concurrent, memoizes one build per mode in a module-level DisposableStack, and tightens assertions to exact stdout/stderr/exitCode/signal. The posted inline finding about the dropped slot() wrapper is the open item; the memoization, disposal ordering, and assertion shapes were checked against test/aot/harness/commands.ts and test/aot/supported.ts and found consistent.

Still open from earlier reviews (1):

  • Unresolved: 1 minor or pre-existing.

Comment on lines +14 to +15
// Each test has folders of its own. The two that ask one executable only read it.
const test = oneAtATime.concurrent;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 (optional) The issue filed at test/aot/entry-point-names.test.ts:124 is back: commit 852c22d removed the slot() wrapper, so developers on a debug Bun without ASAN and few cores again get --aot builds killed at 90 s. With test.concurrent at line 15 all 11 tests start together and build() at line 107 spawns 12 builds with no slot; spawned() kills a build at limitOfARun = 90_000 (test/aot/harness/commands.ts:58) when isASAN is false. Fix: keep every build in this file behind slot() (or another bound on concurrent builds) so no single --aot debug build waits past 90 s on 2-4 cores, while keeping the runs of executables unbounded if the CI timing matters.

Why this was flagged

Trigger: bun bd --asan=off test test/aot/entry-point-names.test.ts on a machine with 2 to 4 cores. test/aot/entry-point-names.test.ts:15 makes every test concurrent and bun test's default max-concurrency is 20, so all tests start at once and build() at line 107 calls spawned() for each of 12 builds, 5 of them with --aot, with no slot() gate. In test/aot/harness/commands.ts:73-79 the timeout is limitOfARun = 90_000 (line 58) because isASAN is false and moreBuildFlags is empty; Bun.spawn kills the child with SIGKILL at line 89 and built() at commands.ts:170-175 fails on the exit code. The PR's own notes measured that without slot() five --aot debug builds on 2 cores take 112 to 122 s each, over that limit. On the base branch the tests ran in series, one build at a time, so no build competed for cores. Commit 1c05c67 had applied the remedy the resolved entry at line 124 asked for; commit 852c22d removed it for CI speed on 4-core release-profile lanes, which does not cover the non-ASAN debug case.

Verification: 852c22d deletes const spawned = (...command) => slot(() => spawnedAtOnce(...command)); build() at test/aot/entry-point-names.test.ts:107 calls it with no gate. test/aot/harness/commands.ts:58 limitOfARun = 90_000, chosen at lines 73-79 when isASAN is false; built() at 170-175 fails on the nonzero exit code. On the base the file used the serial test, so this is new.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is correct, and it is open. I have not pushed a change for it yet, for two reasons.

  1. The reason I gave for removing the slots is not confirmed. With the slots, three CI lanes with four processors took about twice as long (one run for each lane). My explanation is a local trace: four slots, five --aot builds, so the fifth build starts when the first ends. Build 135810 runs the head without the slots. If those lanes do not return to about 3 s, the explanation is wrong and I put the slots back.

  2. If the explanation holds, slots for the builds alone keep the cost. The command that waits in the trace is the fifth --aot build, not a run. A bound equal to the number of processors protects a machine with 2 processors and costs a build length on a machine with 4. A larger bound removes the cost and, on 2 processors, lets four --aot builds run at once, which I have not measured.

One limit of my own numbers: the 112 to 122 s are from a debug build with ASAN and --max-concurrency=20, used in place of a build without a sanitizer. I have not run a debug build without ASAN.

The candidate I see is a gate that applies only to a debug build without a sanitizer, since that is the one configuration with the 90 s limit and slow builds. I will not push it before I can run such a build.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Build 135810 has the three lanes. Same shard in each build, one run each:

lane no slots (135747) slots (135759) head, no slots (135810)
ubuntu 25.04 x64 3.2 s 6.3 s 4.2 s
ubuntu 25.04 aarch64 2.5 s 5.8 s 3.1 s
debian 13 aarch64 3.0 s 6.6 s 4.7 s

I wrote that I put the slots back if these lanes do not return to about 3 s. Two of them did not. As I worded it, the condition is not met.

I keep the slots out all the same, for this reason: the file got slower on all three lanes when the slots went in and faster on all three when they came out, by about 2 s or more each time. The two builds without slots differ by 0.6 to 1.7 s on the same lane, so the direction is clearer than the size. I cannot say how much the slots cost. "Twice as long" was too strong.

This note is still not addressed. A debug build without a sanitizer on few processors can reach the 90 s limit with this file.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant