Skip to content

runtime: split/join/concat slice 4 (string-build -12.6% I) - #13459

Merged
proggeramlug merged 2 commits into
mainfrom
runtime-string-slice4
Oct 29, 2026
Merged

proggeramlug merged 2 commits into
mainfrom
runtime-string-slice4

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Slice 4 of the string-build work (follows #13450). 30-string-build: 7.195B -> 6.291B instructions (-12.6%); the remaining gap to scriptc is split old birth and GC.

  • D, split piece births without root/reload. arena_alloc_gc_old_no_collect (and the existing nursery no-collect form through string_storage_alloc_for_no_collect) serve a piece from a swept same-size hole or from the block that is already open and never reach gc_check_trigger or a block reservation: a non-null return proves nothing moved, so split_by_delimiter keeps the source and result addresses live across the piece allocation. A refusal takes the unchanged rooted, collecting allocation and refreshes both addresses from their handles, so the collection decision is the one the unconditional call would have made.
  • F, string-array .length loops. for (i < a.length) s += a[i].length over a declared string[] is admitted by the indexed-owner region: ordinary unforwarded array without element descriptors, length <= capacity, one preheader scan that every slot is a heap string or an inline ASCII string (a hole, undefined, an object or a non-ASCII inline string declines the whole region before any body effect), then a call-free body (tag test + header load per element). The effect-gated entry poll stays (slice-2 lesson).
  • G, join. The size/validation walk and the copy walk test the heap-string tag first, add into plain u64 totals and copy short pieces inline.
  • js_string_concat_parts_2..5. Count-specialized entries for the operand counts the compiler knows; the operand classification and the copy are unrolled with constant indices (generic over N, selected by n, no per-program code). The generic entry dispatches to them.

Results (qb9, x86-64-v3, KEEP_SYMBOLS, main 88ed1e5 vs head 304a32b)

witness Ir main Ir head delta
30-string-build (callgrind) 7,195,107,053 6,291,485,243 -12.56%
30-string-build (n5 median perf instr / cycles) 7,163,152,238 6,251,569,771 -12.73% / cycles -7.4% (n11: -6.6%)
24-map-set (callgrind) 3,981,178,535 3,981,182,976 +0.0001%
31-json (callgrind) 19,391,283,157 19,389,905,102 -0.007%

All other js-compiled benchmarks: instructions within 0.02% (43-generators -0.30%). n5 cycle deltas on unchanged-instruction benchmarks reach +/-9% on this shared host (n11 re-run: 24-map-set -6.2%, 32-regex +2.2%, same instruction counts), so cycles carry host noise; instructions are exact.
GC_LOG schedule (same kinds, freed/old bytes) and stdout identical on 30, 24, 31. RSS n5 median (KiB): 30-string-build 238,720 -> 238,768, 24-map-set 28,240 -> 28,296, 31-json 149,908 -> 149,880 (no allocation count, size, capacity or generation changes; timing-free).
.text: 30-string-build -1,428 B, 24-map-set +2,152 B, 31-json +27,256 B (+0.43%: the four unrolled concat entries, 2.8-5.9 KB each, replace one 6.1 KB generic entry; reported, not hidden).

Startup, exact callgrind Ir, n3 per arm, deterministic, ASLR off:

witness raw main raw head d controlled main controlled head d
hello 140,644 140,644 0 159,465 159,465 0
commander 21,219,728 21,221,109 +1,381 111,769,396 111,769,325 -71
commander --help 22,778,990 22,783,051 +4,061 117,988,376 117,987,253 -1,123
fastify 159,953,030 159,870,870 -82,160 2,934,775,210 2,934,694,396 -80,814
Richards 42,272,614 42,276,096 +3,482 370,216,452 370,214,586 -1,866
Controlled = the JMS address-independent-key control applied to both arms (canonical keys, path registry, shapes, perex cache) plus an address-independent memcmp/bcmp preload on both arms. Controlled head <= main on all five. The raw positives are layout artifacts: with the JMS control alone commander was +153, all of it in glibc __memcmp_avx2_movbe (13,943 calls from the same callers in both arms; the delta sits in the `(a b)&0xfff > 0xfe0` page-offset branch at 0x1975c0, 89 more calls take it in head); concat is -70 Ir in commander (2 calls).

Tests

  • runtime: string 252, json 171, array 453, arena 133 (two new: the old no-collect entry REFUSES a full open block without reaching the allocation-point trigger, and births the same header as the collecting entry); codegen lib 2589 (new: string-array region scans once, stays call-free, poll precedes the scan; declines stores/calls/rebinding), integration 23.
  • test-files/test_gap_jst6_split_join.ts against Node: split of empty strings/separators/unicode/lone surrogates/very long/20k pieces, join of holey arrays and non-string elements with toString/Symbol, string-array length loops over holes, undefined, null, objects, a Proxy, an accessor element, a subclass, a shifted array, concat with 2..6 operands across every value kind. Passes on main and head.
  • Sabotage: forcing a full collection in the no-collect piece path (D) and dropping the slot-scan verdict (F) both fail that test (piece 0 comes back as "p1"; join no longer equals the source).
  • Node parity 22/22; gap subset 89: 83 pass, the same 6 fail on main. Windows runtime build/sidecar: see the lane report.

…rray length region, fixed-count concat)

D: String.prototype.split births each piece through a no-collect old/young allocation and keeps the source and result addresses live across it; only a refusal roots and reloads.
F: a loop reading array[i].length of a declared string[] runs as one call-free region after a single scan of the slots.
G: array join tests the heap-string tag first and copies short pieces inline.
Count-specialized js_string_concat_parts_2..5 with unrolled operand classification and copy.
@coderabbitai

coderabbitai Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Caution

Review failed

The pull request is closed.

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • 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.

@proggeramlug
proggeramlug marked this pull request as ready for review October 29, 2026 05:57
@proggeramlug
proggeramlug merged commit 2b2964b into main Oct 29, 2026
97 of 103 checks passed
@proggeramlug
proggeramlug deleted the runtime-string-slice4 branch October 29, 2026 06:00
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.

1 participant