mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
An async function called inside LifetimeRegion.run now keeps its region after every await: its fiber records the region that was current at spawn, and the event loop makes that region current while it resumes the fiber. A region is not freed (or attempted) while a fiber that carries it exists, so a region released while its async work is pending keeps taking that work's objects and is freed once the work ends. Before, objects created after an await went to the default region, which is never freed, and kept the request's region allocated for good, so per-request regions in async servers grew without bound. Freed region chunks now go to a per-thread cache (at most 8 MiB resident, the first 256 KiB of each kept cleared, fresh pages past it, no huge-page chunks) instead of being unmapped, so a region per request or per batch no longer maps and faults in new memory every time. Adds the requests.ts region benchmark (concurrent async handlers), corpus 5023 and its region-stats checks, and a chunk-reuse runtime unit check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lifetime-region benchmarks
Programs that build generations of ProgramLifetime objects and free each generation with a LifetimeRegion (@scriptc/experimental). Every variant prints the same result line before its timings.
| Program | Shape |
|---|---|
generations.ts |
Per-file syntax trees and binder symbols in one region per file, checker state (types, signatures, node and symbol links in paged tables, literal and union caches) in one region per generation. edit re-parses one file per generation, all re-parses every file. |
compiler.ts |
A tokenizer, recursive-descent parser, binder and checker over generated sources, rebuilt from scratch in one region per build, like a watch mode that does not reuse syntax trees. |
emit.ts |
A large live graph of ordinary objects with parent links, standing in for a type checker, and a per-file emit that builds its output in a region released right after. |
requests.ts |
Batches of concurrent async request handlers, each in its own region across its awaits: tokenize a JSON body, parse it into a syntax tree that points at its tokens, validate it into issues and an index, render a response, release the region. |
$ node benchmarks/regions/run.mjs --baseline=../scriptc-main --runs=7
Each configuration is timed in five variants, interleaved: on (ProgramLifetime classes, no regions), off (the same classes without implements ProgramLifetime), region, region-plain (the region build run without constructing a region, which measures what supporting regions costs a one-shot run) and Node.js. --baseline builds on and off with another checkout.