Repository navigation
Free the address space after process.exit off the critical path (--detached-exit) - #860
Merged
Merged
Conversation
…tached-exit) `scriptc build|run --detached-exit` opts an executable into handing the address-space teardown after process.exit to a helper process on Linux. The helper shares the address space (CLONE_VM), blocks signals, closes every descriptor but the read end of a pipe and waits for EOF on it. The kernel releases an exiting process's memory (exit_mm) before its files (exit_files), so EOF means the helper holds the last reference; it then exits and the kernel frees the pages there instead of before the parent sees the exit (30-40 ms per GB of 4 KiB pages; tsc-ts 20-110 ms). The same latency mold hides with --fork. It is a build-time choice, not a run-time knob, because the helper keeps the executable mapped (overwriting the binary right after it exits can fail with ETXTBSY) and the memory charged to the cgroup for up to ~100 ms, and the teardown CPU time leaves the parent's rusage. The program object defines `scr_detached_exit = 1`, overriding the runtime's weak default of 0, so no runtime flavor or linker flag is added. The option is part of the early executable cache and route keys; executables only, no effect on other platforms.
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The emitter defines @scr_detached_exit for --detached-exit executables, and the runtime ABI test requires every scr_* symbol the backend can emit to have a prototype or extern data declaration in the header.
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Branch
perf/detached-exit, stacked onperf/worker-fd-table(open after it). One commit.process.exitends in_Exit. The kernel then frees the whole address space before the parent sees the exit: 30–40 ms per GB of 4 KiB pages, about 2.5 ms/GB with THP. For tsc-ts that is 21 ms (tRPC), 46 ms (TypeORM) and 66–110 ms (Playwright) after its last instruction.With
scriptc build --detached-exit(alsoscriptc run), the program'sprocess.exiton Linux first clones a helper that:CLONE_VM);The kernel releases an exiting process's memory (
exit_mm) before its files (exit_files). So EOF means the helper holds the last reference. The helper then exits, and the pages are freed there, after the waiting parent has already seen the exit status. This is the latency mold hides with--fork. The program's output, exit status and descriptors are unchanged: the helper holds no descriptor the program had.Why a build-time option
The helper keeps the executable mapped for up to ~100 ms after exit (overwriting the binary in place then fails with ETXTBSY), the memory stays charged to the cgroup for that long, and the teardown CPU time no longer appears in the parent's
wait4rusage. That has to be the program author's choice, so it is opt-in at build time instead of an environment variable read at run time.How it flows
--detached-exitforbuild/run, executables only (--emitother thanexe,coverage,--libandcache warmreject it). Documented in the CLI help and on the CLI docs page.CompileBaseOptions.detachedExit→ the LLVM emitter defines@scr_detached_exit = constant i8 1in the program object. The runtime (scr_lib.c, Linux) declaresscr_detached_exitas a weak constant 0 and reads it inprocess.exit. The program's strong definition overrides it at link time, so there is no new runtime flavor and no linker flag; builds without the option are unchanged.Measurements (overnight, with the environment-variable version; tsc-ts opted in; not re-measured)
Interleaved, median of 12, same tsc-ts with and without the opt-in:
Tests
emitter.test.ts(the definition appears only with the option, and nothing else in the module changes),executable-cache.test.ts(the option isolates cache entries),output-options.test.ts(non-exe emits reject it),source-output.test.ts(non-executable compile output refuses it): 4 files, 95 tests pass.process.exit(3), built without / with the flag:nmshows the runtime's weakV scr_detached_exit/ the program'sR scr_detached_exit;strace -f -e cloneshows no clone / oneclone(CLONE_VM|SIGCHLD)whose child exits after the parent; same for--optimization=speedand--sanitize. Output and exit code identical.scr_worker.test.ts+scr_alloc.test.ts5/5; differential corpus-t "worker|exit"54/54.bootstrap.js build packages/compiler/src/native/cli.ts --emit=ir): no diagnostics.pnpm lint,oxfmt --checkon touched files: clean. Docs:cd docs && NEXT_DIST_DIR=.next-check pnpm checkpasses (23/23 route tests).