Skip to content

perf: reduce nested worker message serialization overhead - #200

Merged
steipete merged 2 commits into
mainfrom
claude/session-postmessage-string-pool
Oct 24, 2026
Merged

steipete merged 2 commits into
mainfrom
claude/session-postmessage-string-pool

Conversation

@steipete

@steipete steipete commented Oct 24, 2026 •

Copy link
Copy Markdown

Nested session metadata makes Worker.postMessage() miss the flat-object fast path. The general serializer then atomizes value strings, after already paying for cross-thread copies of the rejected primitive prefix. This change uses content-keyed string pooling for in-process messages and checks flat-object eligibility before allocating those copies. Successful flat objects keep the existing isolation path and reserve their exact property count.

Storage and cross-process callers retain canonical Identifier encoding. The pool remains per serialization, with no retained cache, wire-version change, or change to message scheduling.

Paired benchmark

Three separate quiet, on-demand c7i.4xlarge instances (16 vCPUs), Node 26.12.1, with the same verified release-profile candidate executable. Ten interleaved triples per instance; 25,000 warmup and 250,000 measured operations in 1,000-message batches. Only the synchronous send loop is timed; acknowledgments and checksum validation are outside it. Baseline: openclaw-v1.4.4-20261019-ef37c31de3-webkit-a2976f02e6.

Lease Baseline ns/op Candidate ns/op Node ns/op Paired candidate/baseline Reduction
1 3990.316 2749.283 2776.711 0.694257 30.57%
2 3985.646 2628.427 2715.715 0.657071 34.29%
3 4247.839 2827.418 2838.354 0.676730 32.33%

The candidate reduces median paired sender cost by 30.6–34.3% and is near Node's median on each instance. This is a sender-latency result, not an equivalent end-to-end throughput claim: first-instance batch elapsed medians were baseline 1,069.93 ms, candidate 1,040.65 ms, Node 889.06 ms.

All thirty interleaved triples (ns/op):

Lease Pair Baseline ns/op Candidate ns/op Node ns/op
1 1 3990.696 2742.527 2784.345
1 2 4028.732 2733.812 2827.516
1 3 3864.274 2755.153 2671.629
1 4 3948.493 2743.414 2769.077
1 5 4047.445 2699.363 2797.526
1 6 3935.145 2782.876 2546.831
1 7 3983.204 2722.318 3047.063
1 8 3989.935 2789.800 2519.235
1 9 4012.822 2826.111 2677.573
1 10 4101.123 2845.009 2944.813
2 1 4061.114 2686.830 2701.059
2 2 3776.185 2643.815 2568.778
2 3 4007.293 2553.196 2777.473
2 4 4264.202 2598.556 2706.727
2 5 3888.699 2619.037 2496.243
2 6 3899.211 2544.404 2614.145
2 7 3984.028 2549.485 2724.703
2 8 3987.264 2659.944 2914.981
2 9 3888.079 2646.128 2737.730
2 10 4153.951 2637.817 2824.212
3 1 4153.123 2830.412 2620.994
3 2 4328.415 2748.870 2862.016
3 3 4378.681 2819.020 2869.919
3 4 4114.000 2938.638 2869.494
3 5 4242.173 2807.320 2939.720
3 6 4558.593 2808.657 2447.581
3 7 4253.505 2886.628 2933.762
3 8 4185.493 2824.424 2814.691
3 9 4092.972 2858.436 2565.546
3 10 4262.496 2939.571 2683.383

bench/postMessage/postMessage-session-entry.mjs preserves the payload, counts, and sender-timer boundary for reproduction:

node bench/postMessage/postMessage-session-entry.mjs
./build/release/bun bench/postMessage/postMessage-session-entry.mjs

Regression guard

Thirty paired samples per cell, ten on each of the three instances. The decision statistic is the median of all thirty paired ratios, with a fixed ceiling of 1.03. Timed JavaScript, payloads and payload-specific operation counts are unchanged.

Cell Pooled candidate/baseline Pairs
clone/buffer-16k 0.974166 30
clone/buffer-256k 0.996488 30
clone/buffer-4m 1.003113 30
clone/session-entry 0.854824 30
clone/hook-payload 0.940596 30
clone/cause-free-error 0.935329 30
postMessage/buffer-16k 0.941409 30
postMessage/buffer-256k 0.987225 30
postMessage/buffer-4m 1.003317 30
postMessage/session-entry 0.684674 30
postMessage/hook-payload 0.829862 30
postMessage/cause-free-error 0.892927 30

Supplemental flat-object pooled ratios: empty 0.989340; 16-key flat object 1.018603; 24 flattened leaves 0.991893 (30 pairs each).

Validation

  • Native release: 446 passing tests across structured-clone-fastpath, workers/structured-clone, and workers/message-port-pipe; zero failures. Three existing tests are debug/ASAN-gated and do not run in release builds; no skips were added or changed.
  • Separate Node/Bun oracle: 14 cases passed, covering cycles, shared references, indexed keys, null prototypes, nested getter mutation, and clone errors.
  • Wire corpus: 288/288 byte-identical outputs to baseline, with Worker and MessagePort round trips and cross-deserialization in both directions.
  • All 90 session samples and 720 microbenchmark samples completed with correct counts/checksums and natural cleanup. No timing samples were excluded or rerun to obtain a pass.
  • Native CI: Linux x64 and Darwin arm64 passed for head 264b8a6d24e5007b98d1a3c16e1174eabd24bbd5. The CI merge commit has the identical Git tree to that head. Formatting, source lints and JavaScript lint also passed.
  • Codex P2 autoreview: scoped-clean for the complete revised candidate.
  • Upstream search found no existing fix for this nested-object serialization cost.

@steipete
steipete marked this pull request as ready for review October 24, 2026 22:26
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-24T22:27:32.011054Z 264b8a6 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@steipete
steipete merged commit 5d7179b into main Oct 24, 2026
6 checks passed
@steipete
steipete deleted the claude/session-postmessage-string-pool branch October 24, 2026 22:27
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