Optimize JSON string escaping - #3782
Conversation
There was a problem hiding this comment.
Pull request overview
Optimizes Json::stringify string escaping in the json package by adding a no-escape fast path and switching escaping to operate on UTF-16 code units to avoid unnecessary allocations in common cases.
Changes:
- Add a pre-scan fast path in JSON string escaping to return the original string when no escaping is needed.
- Rewrite escaping logic to iterate UTF-16 code units (single pass over code units when escaping is required).
- Add dedicated
Json::stringifybenchmarks to measure escaped vs non-escaped string array performance.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| json/json.mbt | Updates JSON string escaping implementation with a no-escape fast path and UTF-16 code-unit based escaping. |
| json/stringify_escape_bench_test.mbt | Adds benchmarks covering stringify performance for strings requiring no escaping vs various escaping scenarios. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
41ea1dd to
538c916
Compare
|
@mizchi thanks for the optimization. Following #3816, I added an eight-wide UTF-16 SIMD need_escape scan for native/wasm in c79c665, with a scalar fallback and scalar tail. In native release benchmarks, the escape-free cases improved from 77.84 µs to 60.26 µs and from 78.46 µs to 59.05 µs (about 23–25% faster); inputs that actually require escaping were effectively unchanged. The pushed commit includes block/tail and surrogate-boundary coverage, and the PR checks can now validate it across the repository. |
2abfb2a to
bdd315a
Compare
Add quickcheck properties pinning the SIMD need_escape against the scalar reference, escape against a per-code-unit model and the real parser, and an exhaustive sweep of escapable code units across SIMD block boundaries. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bdd315a to
2ab0bc6
Compare
|
Rebased onto latest main and resolved the conflicts. Conflict resolution: main now ships its own portable More rigorous testing: added
Fresh benchmarks after the rebase (Apple Silicon,
The fast-path win grew from ~3.7x to ~6.8x because main's Validation: |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Follow-up: dropped the 17 inferable Re-reviewed the full branch with Codex CLI (
|
Summary
Json::stringifybenchmarks for escaped and non-escaped string arraysContext
This follows the dependency hotspot analysis:
https://gist.github.com/mizchi/28f382dd0d9c7c1cb0ef42b202f7f24e
jsonis one of the higher-priority non-builtin packages by dependency fan-out, andJson::stringifysits on top ofStringBuilder/string output paths. The previous escape helper allocated and copied through aStringBuildereven when a string contained no escapable characters. This is common for JSON keys and ordinary ASCII values.Benchmarks
Measured with native release benches. Before is
upstream/main@607c2745; after is this PR head69942b73.Json::stringify strings no escape n=2048Json::stringify strings slash no escape n=2048Json::stringify strings slash escaped n=2048Json::stringify strings quotes controls n=2048Validation
moon fmtmoon infomoon test -p moonbitlang/core/json --target allmoon check --target allmoon bench -p moonbitlang/core/json -f stringify_escape_bench_test.mbt --target native --release