diff --git a/CLAUDE.md b/CLAUDE.md index 8b76e5fcc0..a208f8f726 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Perry is a native TypeScript compiler written in Rust that compiles TypeScript source code directly to native executables. It uses SWC for TypeScript parsing and LLVM for code generation. -**Current Version:** 0.5.1324 +**Current Version:** 0.5.1325 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index d9dfac173f..1c75e890b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5547,7 +5547,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perry" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "base64", @@ -5607,14 +5607,14 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "serde", ] [[package]] name = "perry-audio-miniaudio" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "cc", "libc", @@ -5622,7 +5622,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "inkwell", @@ -5639,7 +5639,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "perry-hir", @@ -5647,7 +5647,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "perry-hir", @@ -5655,7 +5655,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "perry-dispatch", @@ -5664,7 +5664,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "perry-hir", @@ -5672,7 +5672,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "base64", @@ -5684,7 +5684,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "perry-hir", @@ -5692,7 +5692,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "async-trait", @@ -5721,14 +5721,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "serde", "serde_json", @@ -5736,7 +5736,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1324" +version = "0.5.1325" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5747,7 +5747,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "clap", @@ -5762,7 +5762,7 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "block2", "objc2", @@ -5772,7 +5772,7 @@ dependencies = [ [[package]] name = "perry-ext-argon2" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "argon2", "perry-ffi", @@ -5780,7 +5780,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "reqwest", @@ -5789,7 +5789,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "bcrypt", "perry-ffi", @@ -5797,7 +5797,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "rusqlite", @@ -5805,7 +5805,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "scraper", @@ -5813,7 +5813,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "perry-runtime", @@ -5821,7 +5821,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "chrono", "cron", @@ -5831,7 +5831,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "chrono", "perry-ffi", @@ -5839,7 +5839,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "rust_decimal", @@ -5847,7 +5847,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "serde_json", @@ -5855,7 +5855,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "rand 0.10.1", @@ -5863,7 +5863,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "perry-runtime", @@ -5871,14 +5871,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "bytes", "http-body-util", @@ -5896,7 +5896,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "bytes", "lazy_static", @@ -5909,7 +5909,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "bytes", "h2", @@ -5933,7 +5933,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "lazy_static", "perry-ffi", @@ -5943,7 +5943,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "base64", "jsonwebtoken", @@ -5954,7 +5954,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "lru", "perry-ffi", @@ -5963,7 +5963,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "chrono", "perry-ffi", @@ -5971,7 +5971,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "bson", "futures-util", @@ -5983,7 +5983,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "chrono", "perry-ffi", @@ -5993,7 +5993,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "nanoid", "perry-ffi", @@ -6002,7 +6002,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "bytes", "perry-ffi", @@ -6015,7 +6015,7 @@ dependencies = [ [[package]] name = "perry-ext-node-forge" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "const-oid 0.9.6", "der 0.7.10", @@ -6034,7 +6034,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "lettre", "perry-ffi", @@ -6044,7 +6044,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "printpdf", @@ -6052,7 +6052,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "sqlx", @@ -6061,7 +6061,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "governor", "perry-ffi", @@ -6069,7 +6069,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "fast_image_resize", "image", @@ -6079,14 +6079,14 @@ dependencies = [ [[package]] name = "perry-ext-slugify" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-streams" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "lazy_static", "perry-ffi", @@ -6095,7 +6095,7 @@ dependencies = [ [[package]] name = "perry-ext-undici" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "perry-runtime", @@ -6104,7 +6104,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "uuid", @@ -6112,7 +6112,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ffi", "regex", @@ -6122,7 +6122,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "futures-util", "lazy_static", @@ -6135,7 +6135,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "brotli", "flate2", @@ -6145,7 +6145,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "dashmap", "once_cell", @@ -6154,7 +6154,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "perry-api-manifest", @@ -6172,7 +6172,7 @@ dependencies = [ [[package]] name = "perry-parser" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "perry-diagnostics", @@ -6184,7 +6184,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "base64", @@ -6226,14 +6226,14 @@ dependencies = [ [[package]] name = "perry-runtime-static" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-runtime", ] [[package]] name = "perry-stdlib" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "aes 0.8.4", "aes 0.9.1", @@ -6328,14 +6328,14 @@ dependencies = [ [[package]] name = "perry-stdlib-static" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-stdlib", ] [[package]] name = "perry-transform" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "perry-hir", @@ -6344,14 +6344,14 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ui-model", ] [[package]] name = "perry-ui-android" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "base64", "itoa", @@ -6368,7 +6368,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "rand 0.10.1", "serde", @@ -6378,7 +6378,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "base64", "cairo-rs 0.22.0", @@ -6401,7 +6401,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "base64", "block2", @@ -6417,7 +6417,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "base64", "block2", @@ -6432,7 +6432,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1324" +version = "0.5.1325" [[package]] name = "perry-ui-test" @@ -6443,11 +6443,11 @@ dependencies = [ [[package]] name = "perry-ui-testkit" -version = "0.5.1324" +version = "0.5.1325" [[package]] name = "perry-ui-tvos" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "base64", "block2", @@ -6463,7 +6463,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "base64", "block2", @@ -6479,7 +6479,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "block2", "libc", @@ -6492,7 +6492,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "base64", "libc", @@ -6509,14 +6509,14 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "perry-ui-windows", ] [[package]] name = "perry-updater" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "anyhow", "base64", @@ -6532,7 +6532,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1324" +version = "0.5.1325" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index ad9340955a..17922aaa6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -315,7 +315,7 @@ codegen-units = 16 codegen-units = 16 [workspace.package] -version = "0.5.1324" +version = "0.5.1325" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry" diff --git a/changelog.d/7565-iterator-no-silent-truncation.md b/changelog.d/7567-iterator-no-silent-truncation.md similarity index 100% rename from changelog.d/7565-iterator-no-silent-truncation.md rename to changelog.d/7567-iterator-no-silent-truncation.md diff --git a/changelog.d/7566-macos-gap-snapshot-fallback.md b/changelog.d/7568-macos-gap-snapshot-fallback.md similarity index 100% rename from changelog.d/7566-macos-gap-snapshot-fallback.md rename to changelog.d/7568-macos-gap-snapshot-fallback.md diff --git a/changelog.d/7572-plan-resync-fragment-numbers.md b/changelog.d/7572-plan-resync-fragment-numbers.md new file mode 100644 index 0000000000..f2b8689cbb --- /dev/null +++ b/changelog.d/7572-plan-resync-fragment-numbers.md @@ -0,0 +1,51 @@ +### Documentation + +- **The engine plan's headline numbers were stale, and two changelog fragments + collided with real PR numbers (#7572).** Documentation and metadata only. + + `docs/engine-plan.md` still listed the two worst rows of the v0.5.1299 sweep at + their pre-fix values, both of which have since moved by an order of magnitude: + `object_deep_clone` from 657.0 ms / 37.5× bun to **40 ms** (~2.3× bun, and + **0.67× node — a win**) via #7540, and `map_1m` from 1233.7 ms / 4.8× bun to + **309.1 ms** (1.40× bun, **0.96× node — a win**) via #7561. The + `json_polyglot` `field_access` inversion — the optimized configuration running + 2.2× *slower* than the unoptimized one — is closed by #7537 and #7539: + 2984 ms σ 136 / 219 MB becomes **1809 ms σ 17.3 / 155 MB**, an 8.3× collapse in + σ, and turning the tape on is no longer worse than turning it off. + + **A stale plan is not cosmetic — it is how effort gets spent on a + non-problem.** Three times this campaign a ticket was picked up and profiled + from a headline number that had already collapsed: #7510 (33.6% → 11%), + `layout_forget_object` (14.5% → 3.0% → 1.7%), and `layout_note_slot` + (7.5% → 0.03%, correctly closed with **no code at all**). The re-synced + section now carries that instruction in place: re-measure the row before + profiling it. + + The v0.5.1299 table is **kept intact rather than overwritten**. It was a single + measurement event at 11 runs per cell; the fixes above were each measured + individually, on the same pinned quiet mini, as part of the change that made + them. Splicing individually measured rows back into a sweep table would claim a + coherence the numbers do not have, so fixed rows are annotated in place and the + deltas live in their own section attributed to the PR that moved them. The + artifact still owes a fresh sweep, which stays blocked on #7475. + + Also records #7566's landing (workstream A of the #7469 construction campaign: + the inline bump allocator at `new` sites inside loops, 1.81× on `churn_alloc` + at +0 bytes for non-loop sites, and the honest 1.4% `tree` cost), together with + the finding that the measurement justifying the previous default had *inverted* + without anyone re-checking it — and the Mach-O negative result, that there is + no local-exec TLS model and `-Ztls-model=local-exec` leaves the `blr` through + the TLV descriptor byte-identical. + + Separately, two fragments were named with an issue number rather than their PR + number, landing on numbers belonging to different changes: + `7565-iterator-no-silent-truncation.md` → **7567** (#7565 is the + `_tlv_get_addr` change) and `7566-macos-gap-snapshot-fallback.md` → **7568** + (#7566 is the inline bump allocator). Left alone, `cut_release_notes.sh` folds + them under the wrong PR, and two distinct changes sharing a number make the + notes unreadable at exactly the point nobody can still check them. + + The rule is worth stating because the failure is systematic, not careless: a + fragment written *before* its PR exists has to guess the number. Open the PR + first and name the fragment from the number it is assigned — which is how this + entry was written. diff --git a/docs/engine-plan.md b/docs/engine-plan.md index 17b8c2363f..a94d1a1913 100644 --- a/docs/engine-plan.md +++ b/docs/engine-plan.md @@ -6,7 +6,10 @@ every dated status section, incident narrative and superseded sequencing lives in [`engine-plan-history.md`](engine-plan-history.md); this file holds only the current state and the remaining work so it stays readable across context loads. -Last synced **2026-08-06**, after the v0.5.1299 public-baseline sweep. +Last synced **2026-08-07** (v0.5.1324). The v0.5.1299 public-baseline sweep is +kept as the baseline measurement event; rows fixed since are annotated in place +rather than overwritten, because they were measured individually rather than in +a fresh sweep. | Concern | Detail lives in | |---|---| @@ -75,6 +78,36 @@ A declared class costing **63% more than an object literal** is backwards and is tracked as a suspected defect (**#7512**), with an explicit off-ramp: if the cause lands in the layout or barrier subsystems it closes as a duplicate. +**Workstream A has landed (#7566, v0.5.1324): the inline bump allocator is back +at `new` sites inside loop bodies**, outlined everywhere else. + +| bench | outlined | gated | ceiling (all-inline) | +|---|--:|--:|--:| +| `churn_alloc` | 1.32 s | 0.73 s (**1.81x**) | 1.81x | +| `push_cls` | 1.30 s | 0.72 s (**1.81x**) | 1.78x | +| `churn` | 1.62 s | 1.04 s (**1.56x**) | 1.56x | +| `tree` | 8.97 s | 9.10 s (**0.986x**) | 1.05x | + +It reaches the full unconditional-inline ceiling on the allocation-heavy shapes +at **+0 bytes for every site not in a loop** (verified by per-function IR +differential, not by assertion). `tree` pays **1.4%** — it allocates in loops so +it inlines, but its time is dominated by copying and promotion, so it takes the +bloat without the win. That is the honest cost of a static proxy. + +**The measurement that justified the previous default had inverted, and nobody +had re-checked it.** The outlined form was made default on "−45 IR lines/site +AND ~17% faster"; the size half still holds (~268 bytes/site) but the speed half +is now **1.81x the wrong way**, because everything *around* the allocation got +cheaper (#7474, #7486, #7487, #7501, #7525, #7532, #7535, #7536, #7552) until +the surviving FFI call dominated what the inline bump's bloat costs. A default +chosen on a measurement is only as current as that measurement. + +Recorded so it is not re-attempted: **Mach-O has no local-exec TLS model.** +Building the entire runtime with `-Ztls-model=local-exec` leaves the `blr` +through the TLV descriptor byte-identical (1.02x). Per-call cost is already at +the plain-global floor — only the *count* of resolutions can be reduced, which +is what inlining does and what #7565 did structurally. + **Two traps recorded here because they cost real time.** `PERRY_WRITE_BARRIERS=0` **cannot** bound barrier cost — it makes `churn_alloc` *slower* (2.44 → 5.21 s) because it also switches the collector out of evacuating mode; the 16.1% is @@ -92,11 +125,11 @@ which topped out at 6.27x and predated two kernels running at all: | kernel | perry | bun | node | perry/bun | owner | |---|--:|--:|--:|--:|---| -| **object_deep_clone** | 657.0 ms | 17.5 | 56.9 | **37.5x** | **#7533** | +| **object_deep_clone** | 657.0 ms | 17.5 | 56.9 | **37.5x** | ✅ **fixed — see below** | | **promise_all_chains** | 259.7 ms | 22.7 | 64.0 | **11.4x** | unowned | | json_parse_1mb | 438.2 ms | 68.1 | 127.1 | 6.4x | unowned | | batch | 127.8 ms | 26.5 | 74.8 | 4.8x | unowned | -| map_1m | 1233.7 ms | 256.5 | 320.1 | 4.8x | unowned (largest absolute) | +| map_1m | 1233.7 ms | 256.5 | 320.1 | 4.8x | ✅ **fixed — see below** | | string_template_interp | 106.9 ms | 41.6 | 100.6 | 2.6x | unowned | | json_stringify_1mb | 97.3 ms | 38.5 | 95.1 | 2.5x | unowned | | string_concat_csv | 51.3 ms | 27.1 | 82.3 | 1.9x | borderline | @@ -106,10 +139,38 @@ which topped out at 6.27x and predated two kernels running at all: | **date_format_parse** | 36.0 ms | 44.8 | 116.3 | **0.80x** | **win** | **`object_deep_clone` and `promise_all_chains` are new to this table because -they were CRASHING, not slow** — fixed today by #7495 and #7516/#7529. Their -first-ever measurement makes deep clone the worst cell by 3x over the next. -#7533 carries the profile-first instruction and an explicit A/B against -`f06270d06` to establish how much predates today's rooting stack. +they were CRASHING, not slow** — fixed by #7495 and #7516/#7529. Their +first-ever measurement made deep clone the worst cell by 3x over the next. + +#### Landed since that sweep — the two worst rows are gone + +The table above is a single measurement *event* (v0.5.1299, 11 runs per cell) +and is kept intact as the baseline. The rows below were re-measured +individually on the same pinned quiet mini as part of the fix that moved them, +so they are **not** interchangeable with a fresh sweep — the artifact still owes +one, tracked under the #7475 blocker further down. + +| kernel | sweep | now | vs bun | vs node | landed | +|---|--:|--:|--:|--:|---| +| **object_deep_clone** | 657.0 ms | **40 ms** | 37.5x → **~2.3x** | 11.5x → **0.67x (win)** | #7540 (closes #7533) | +| **map_1m** | 1233.7 ms | **309.1 ms** | 4.8x → **1.40x** | 3.9x → **0.96x (win)** | #7561 | + +Both were single structural defects rather than broad slowness, which is why +each moved by an order of magnitude instead of a few percent: + +- **deep clone** — `[...arr]` on an ordinary dense array ran the full iterator + protocol. 90.45% of the whole process sat in `array_from_spread_value`, and + the identical copy spelled `Array.from(tags)` cost **66x less**. A 3-element + spread was ~25 allocations against bun's one allocation and a 24-byte memcpy. +- **map_1m** — `for (const v of m.values())` was 512 ms of the kernel; as a + delete-safe index walk over the flat entries it is **5 ms**. Perry already won + lookup before the fix (76 ms vs node 115, bun 103). + +**Read this section before picking up any row above.** Three times this campaign +a ticket was worked from a headline number that had already collapsed — #7510 +(33.6% → 11%), `layout_forget_object` (14.5% → 3.0% → 1.7%), and `layout_note_slot` +(7.5% → 0.03%, correctly closed with **no code at all**). Re-measure the row +before profiling it. ### JSON polyglot legs — the tape is a net negative on scans @@ -121,12 +182,32 @@ Same run. `roundtrip` is the crown jewel and `field_access` is the problem: | field_access | **2984 ms** (219 MB, sigma 136) | **1350 ms** (61 MB) | 218 | 380 | 183 | Perry **wins roundtrip** against both JS runtimes and lands within ~8% of Rust -serde_json. But on `field_access` the **optimized configuration is 2.2x SLOWER -than the unoptimized one** and carries 3.6x the RSS — the lazy tape is a net -negative on scan-shaped access, and its sigma of 136 (against every other row's -under 5) says the cost is data-dependently variable. The 1350 ms idiomatic row -is the floor any fix must beat: simply declining the tape for this shape would -already be 2.2x better. Tracked in **#7478**. +serde_json. `field_access` was the standing problem: the optimized configuration +was 2.2x SLOWER than the unoptimized one at 3.6x the RSS, with a sigma of 136 +against every other row's under 5. + +**That inversion is now closed** (#7478 → #7537 early batch flip, then #7539 +tape side-allocation), measured on the same host: + +| `field_access` | median | sigma | peak RSS | +|---|--:|--:|--:| +| sweep (v0.5.1299) | 2984 ms | 136 | 219 MB | +| after #7537 | 2043 ms | 146 | 195 MB | +| **after #7539** | **1809 ms** | **17.3** | **155 MB** | + +Sigma collapses **8.3x** — that was the headline symptom, not the median — and +the decisive result is that **turning the tape ON is no longer worse than +turning it OFF**. The tape-off arm still carries sigma 117.2 and 168 MB, so the +residual variance and footprint belong to the generational collector's behaviour +on this workload and have nothing left to do with the tape. The GC trace agrees +to the cycle: 19 cycles / 9 full / 6 `old_gen_bytes` becomes **14 / 5 / 2**, +which is the `PERRY_JSON_TAPE=0` arm's profile exactly. + +`roundtrip` — the memcpy path this must not regress — **improved**, 201 → 193 ms, +with peak old-generation in-use down 39.6 → 14.1 MB. + +Still open: 1809 ms has not reached the 1350 ms idiomatic floor, and the gap is +now collector behaviour rather than tape design. ### Gates and blockers