Skip to content

perf: reuse validated JSON values for version operations - #114

Merged
fengmk2 merged 1 commit into
masterfrom
perf/reuse-validated-json
Oct 17, 2026
Merged

fengmk2 merged 1 commit into
masterfrom
perf/reuse-validated-json

Conversation

@fengmk2

@fengmk2 fengmk2 commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Version lookups and comparisons scan and validate the same versions object multiple times. The implementation now reuses validated sonic-rs values to avoid these repeated scans. It preserves input validation and byte positions for JSON edits.

Add benchmarks for version lookup, comparison, and mutation to CodSpeed. Make the metadata benchmarks compare the same fields, and remove the fixed speedup claim for getMetaInfo().

The table shows median latency in milliseconds from the second paired comparison. Both sides used the same fixtures and tinybench@5.1.0. Both native binaries used release mode. The machine used an Apple M4 Max, macOS arm64, and Node.js v24.22.0.

Each Package operation includes construction. Each mutation creates a new JSONBuilder. Speedup equals the previous latency divided by the new latency.

Operation 22 MiB: before → after (ms) Speedup 89 MiB: before → after (ms) Speedup
getLatestVersion() 38.89 → 16.37 2.38× 139.10 → 58.86 2.36×
diff() 39.20 → 16.68 2.35× 142.79 → 60.06 2.38×
Read sync fields and latest version 48.43 → 26.51 1.83× 185.59 → 103.92 1.79×
Add a version with JSONBuilder.setIn() 47.05 → 35.45 1.33× 170.71 → 129.51 1.32×
Delete the latest version with JSONBuilder.deleteIn() 40.02 → 30.08 1.33× 143.22 → 103.86 1.38×
getMetaInfo() 27.04 → 26.75 1.01× 108.07 → 111.02 0.97×

Across both paired comparisons, version lookup and comparison improve by 2.35–2.48× on these large fixtures, including construction. The gain depends on the operation; getMetaInfo() remains approximately unchanged.

Summary by CodeRabbit

  • Benchmarking
    • Added a standalone hot-path benchmark and included it in the standard benchmark run. Results can be saved with environment and input details.
    • Expanded benchmark verification across package metadata and version keys.
  • Bug Fixes
    • Property edits preserve whitespace around numeric values, and reported value positions exclude trailing whitespace.
    • Editing or constructing packages with malformed JSON now raises an error.
  • Documentation
    • Clarified that metadata lookup performance varies with document size and requested fields, and added instructions for running and comparing benchmarks.

@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 981b92f9-0257-4490-849c-cb403f620bad
📥 Commits

Reviewing files that changed from the base of the PR and between a3b8402 and 4fe81af.

📒 Files selected for processing: 11
  • .github/workflows/codspeed.yml
  • README.md
  • __test__/builder.spec.ts
  • __test__/package.spec.ts
  • benchmark/bench_hot_paths.ts
  • benchmark/bench_real_case.ts
  • benchmark/real_case_worker.ts
  • index.d.ts
  • package.json
  • src/json.rs
  • src/package.rs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

Package operations now use lazy-value iteration for object and array data. Byte-position results exclude trailing JSON whitespace. The PR adds edge-case tests, updates real-case benchmark verification, and adds a hot-path benchmark suite.

Changes: Package parsing and benchmarks

Layer / File(s) Summary
Lazy-value access and byte positions
src/package.rs, src/json.rs, __test__/builder.spec.ts, __test__/package.spec.ts, README.md, index.d.ts
Package operations use lazy-value iterators, and byte-position calculation excludes trailing JSON whitespace. Tests cover numeric-property deletion, malformed JSON, version lookup, and byte ranges. Performance documentation no longer makes a fixed speed claim.
Real-case benchmark verification
benchmark/bench_real_case.ts, benchmark/real_case_worker.ts
The benchmark compares both sonic-rs paths with JSON.parse for the listed metadata fields and sorted version keys. It also verifies the super-large fixture.
Hot-path benchmark suite
benchmark/bench_hot_paths.ts, package.json, .github/workflows/codspeed.yml, README.md
The new benchmark measures construction, cold and warm operations, field access, and version changes across three fixtures. The package scripts and workflow run it, and the README documents running it and saving results.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Refactor

Sequence Diagram(s)

sequenceDiagram
  participant Fixture as JSON fixture
  participant Bench as bench_hot_paths.ts
  participant Package as Package
  participant Tinybench
  participant Output as JSON output
  Fixture->>Bench: Provide fixture data
  Bench->>Package: Construct and validate operations
  Bench->>Tinybench: Register and run benchmark tasks
  Tinybench-->>Bench: Return task results
  Bench->>Output: Write metadata and completed results
Loading

Suggested reviewers: elrrrrrrr

Merge Risk: ⚪ Minimal · up to 4fe81

No merge-blocking issue was established; merge after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 8 files. (3 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main performance change: reusing validated JSON values for version operations.
Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 8 files. (3 skipped: 3 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 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.

@fengmk2
fengmk2 marked this pull request as ready for review October 17, 2026 06:59
Copilot AI balanced review requested due to automatic review settings October 17, 2026 06:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@github-actions

Copy link
Copy Markdown

📦 napi-rs Build Size Comparison

File Base Size PR Size Diff %
📈 packument.linux-x64-gnu.node 949.96 KB 953.96 KB +4.00 KB +.42%

Total: 949.96 KB → 953.96 KB (+4.00 KB, +.42%)

Compared using x86_64-unknown-linux-gnu target

@fengmk2

fengmk2 commented Oct 17, 2026

Copy link
Copy Markdown
Member Author

@codex review

@github-actions

Copy link
Copy Markdown

⚡ Benchmark Results

Click to expand benchmark output
$ node scripts/tsrs.js ⊘ cache disabled

$ napi build --platform --release --esm ⊘ cache disabled
2026-10-17T07:00:15.994Z napi:build napi build command receive options: {
  target: undefined,
  cwd: undefined,
  manifestPath: undefined,
  configPath: undefined,
  packageJsonPath: undefined,
  targetDir: undefined,
  outputDir: undefined,
  platform: true,
  jsPackageName: undefined,
  constEnum: undefined,
  jsBinding: undefined,
  noJsBinding: undefined,
  dts: undefined,
  dtsHeader: undefined,
  noDtsHeader: undefined,
  dtsCache: true,
  esm: true,
  strip: undefined,
  release: true,
  verbose: undefined,
  bin: undefined,
  package: undefined,
  profile: undefined,
  crossCompile: undefined,
  useCross: undefined,
  useNapiCross: undefined,
  watch: undefined,
  features: undefined,
  allFeatures: undefined,
  noDefaultFeatures: undefined,
  cargoOptions: []
}
2026-10-17T07:00:16.052Z napi:build Set features flags: 
2026-10-17T07:00:16.052Z napi:build   []
2026-10-17T07:00:16.052Z napi:build Set compiling target to: 
2026-10-17T07:00:16.052Z napi:build   �[32mx86_64-unknown-linux-gnu�[39m
2026-10-17T07:00:16.053Z napi:build Set envs: 
2026-10-17T07:00:16.053Z napi:build   �[32mNAPI_TYPE_DEF_TMP_FOLDER=/home/runner/work/packument/packument/target/napi-rs/packument-7c6aeb97�[39m
2026-10-17T07:00:16.053Z napi:build Start building crate: packument
2026-10-17T07:00:16.053Z napi:build   �[32mcargo build --target x86_64-unknown-linux-gnu --release�[39m
�[1m�[92m   Compiling�[0m proc-macro2 v1.0.103
�[1m�[92m   Compiling�[0m quote v1.0.42
�[1m�[92m   Compiling�[0m unicode-ident v1.0.22
�[1m�[92m   Compiling�[0m cfg-if v1.0.4
�[1m�[92m   Compiling�[0m dtor-proc-macro v0.0.6
�[1m�[92m   Compiling�[0m futures-sink v0.3.31
�[1m�[92m   Compiling�[0m futures-core v0.3.31
�[1m�[92m   Compiling�[0m napi-build v2.3.1
�[1m�[92m   Compiling�[0m serde_core v1.0.228
�[1m�[92m   Compiling�[0m libc v0.2.177
�[1m�[92m   Compiling�[0m futures-channel v0.3.31
�[1m�[92m   Compiling�[0m pin-utils v0.1.0
�[1m�[92m   Compiling�[0m ctor-proc-macro v0.0.7
�[1m�[92m   Compiling�[0m pin-project-lite v0.2.16
�[1m�[92m   Compiling�[0m memchr v2.7.6
�[1m�[92m   Compiling�[0m futures-task v0.3.31
�[1m�[92m   Compiling�[0m zerocopy v0.8.27
�[1m�[92m   Compiling�[0m slab v0.4.11
�[1m�[92m   Compiling�[0m serde v1.0.228
�[1m�[92m   Compiling�[0m futures-io v0.3.31
�[1m�[92m   Compiling�[0m syn v2.0.110
�[1m�[92m   Compiling�[0m version_check v0.9.5
�[1m�[92m   Compiling�[0m getrandom v0.3.4
�[1m�[92m   Compiling�[0m ahash v0.8.12
�[1m�[92m   Compiling�[0m thiserror v2.0.17
�[1m�[92m   Compiling�[0m unicode-segmentation v1.12.0
�[1m�[92m   Compiling�[0m ref-cast v1.0.25
�[1m�[92m   Compiling�[0m convert_case v0.9.0
�[1m�[92m   Compiling�[0m dtor v0.1.1
�[1m�[92m   Compiling�[0m napi v3.5.2
�[1m�[92m   Compiling�[0m libloading v0.9.0
�[1m�[92m   Compiling�[0m once_cell v1.21.3
�[1m�[92m   Compiling�[0m bytes v1.11.1
�[1m�[92m   Compiling�[0m semver v1.0.27
�[1m�[92m   Compiling�[0m simdutf8 v0.1.5
�[1m�[92m   Compiling�[0m napi-sys v3.1.1
�[1m�[92m   Compiling�[0m ctor v0.6.1
�[1m�[92m   Compiling�[0m packument v0.1.0 (/home/runner/work/packument/packument)
�[1m�[92m   Compiling�[0m sonic-simd v0.1.2
�[1m�[92m   Compiling�[0m napi-derive-backend v3.0.2
�[1m�[92m   Compiling�[0m sonic-number v0.1.0
�[1m�[92m   Compiling�[0m rustc-hash v2.1.1
�[1m�[92m   Compiling�[0m nohash-hasher v0.2.0
�[1m�[92m   Compiling�[0m itoa v1.0.15
�[1m�[92m   Compiling�[0m bitflags v2.10.0
�[1m�[92m   Compiling�[0m bumpalo v3.19.0
�[1m�[92m   Compiling�[0m ryu v1.0.20
�[1m�[92m   Compiling�[0m futures-macro v0.3.31
�[1m�[92m   Compiling�[0m serde_derive v1.0.228
�[1m�[92m   Compiling�[0m thiserror-impl v2.0.17
�[1m�[92m   Compiling�[0m futures-util v0.3.31
�[1m�[92m   Compiling�[0m ref-cast-impl v1.0.25
�[1m�[92m   Compiling�[0m napi-derive v3.3.3
�[1m�[92m   Compiling�[0m faststr v0.2.32
�[1m�[92m   Compiling�[0m sonic-rs v0.5.6
�[1m�[92m   Compiling�[0m futures-executor v0.3.31
�[1m�[92m   Compiling�[0m futures v0.3.31
�[1m�[92m    Finished�[0m `release` profile [optimized] target(s) in 17.21s
2026-10-17T07:00:33.404Z napi:build �[32mBuild crate packument successfully!�[39m
2026-10-17T07:00:33.404Z napi:build Try to create output directory:
2026-10-17T07:00:33.404Z napi:build   �[32m/home/runner/work/packument/packument�[39m
2026-10-17T07:00:33.404Z napi:build Output directory created
2026-10-17T07:00:33.404Z napi:build Copy artifact from: [/home/runner/work/packument/packument/target/x86_64-unknown-linux-gnu/release/libpackument.so]
2026-10-17T07:00:33.404Z napi:build Old artifact found, remove it first
2026-10-17T07:00:33.410Z napi:build Copy artifact to:
2026-10-17T07:00:33.410Z napi:build   �[32m/home/runner/work/packument/packument/packument.linux-x64-gnu.node�[39m
2026-10-17T07:00:33.525Z napi:build Writing type def to:
2026-10-17T07:00:33.525Z napi:build   �[32m/home/runner/work/packument/packument/index.d.ts�[39m
2026-10-17T07:00:33.627Z napi:build Writing js binding to:
2026-10-17T07:00:33.627Z napi:build   �[32m/home/runner/work/packument/packument/index.js�[39m

$ node benchmark/bench_hot_paths.ts ⊘ cache disabled
┌─────────┬───────────────────────────────────────────────┬─────────────────────┬───────────────────────┬────────────────────────┬────────────────────────┬─────────┐
│ (index) │ Task name                                     │ Latency avg (ns)    │ Latency med (ns)      │ Throughput avg (ops/s) │ Throughput med (ops/s) │ Samples │
├─────────┼───────────────────────────────────────────────┼─────────────────────┼───────────────────────┼────────────────────────┼────────────────────────┼─────────┤
│ 0       │ 'a.json: construct'                           │ '59503 ± 0.09%'     │ '58929 ± 111.00'      │ '16829 ± 0.07%'        │ '16970 ± 32'           │ 8403    │
│ 1       │ 'a.json: readme (cold)'                       │ '101598 ± 0.34%'    │ '99420 ± 370.00'      │ '9911 ± 0.17%'         │ '10058 ± 38'           │ 4922    │
│ 2       │ 'a.json: getMetaInfo (cold)'                  │ '179239 ± 0.77%'    │ '174257 ± 1787.5'     │ '5649 ± 0.25%'         │ '5739 ± 59'            │ 2790    │
│ 3       │ 'a.json: getLatestVersion (cold)'             │ '101498 ± 0.09%'    │ '100481 ± 300.00'     │ '9861 ± 0.08%'         │ '9952 ± 30'            │ 4927    │
│ 4       │ 'a.json: getLatestVersion (warm)'             │ '41504 ± 0.16%'     │ '41012 ± 101.00'      │ '24159 ± 0.07%'        │ '24383 ± 60'           │ 12048   │
│ 5       │ 'a.json: diff (cold)'                         │ '111182 ± 0.37%'    │ '109906 ± 501.50'     │ '9015 ± 0.08%'         │ '9099 ± 42'            │ 4498    │
│ 6       │ 'a.json: diff (warm)'                         │ '51812 ± 0.10%'     │ '51418 ± 351.00'      │ '19337 ± 0.08%'        │ '19448 ± 134'          │ 9651    │
│ 7       │ 'a.json: sync fields (cold)'                  │ '276064 ± 0.56%'    │ '270963 ± 2428.0'     │ '3645 ± 0.25%'         │ '3691 ± 33'            │ 1812    │
│ 8       │ 'a.json: add version'                         │ '191876 ± 2.52%'    │ '166866 ± 10430'      │ '5494 ± 0.66%'         │ '5993 ± 399'           │ 2606    │
│ 9       │ 'a.json: delete latest version'               │ '140019 ± 2.37%'    │ '124457 ± 2733.0'     │ '7450 ± 0.41%'         │ '8035 ± 180'           │ 3571    │
│ 10      │ 'npm.json: construct'                         │ '10601865 ± 0.13%'  │ '10590922 ± 16385'    │ '94 ± 0.13%'           │ '94 ± 0'               │ 48      │
│ 11      │ 'npm.json: readme (cold)'                     │ '14826093 ± 0.42%'  │ '14780223 ± 17115'    │ '67 ± 0.40%'           │ '68 ± 0'               │ 34      │
│ 12      │ 'npm.json: getMetaInfo (cold)'                │ '21176168 ± 0.17%'  │ '21152270 ± 34122'    │ '47 ± 0.17%'           │ '47 ± 0'               │ 24      │
│ 13      │ 'npm.json: getLatestVersion (cold)'           │ '19504843 ± 2.92%'  │ '19036770 ± 48408'    │ '51 ± 2.34%'           │ '53 ± 0'               │ 26      │
│ 14      │ 'npm.json: getLatestVersion (warm)'           │ '8445668 ± 0.20%'   │ '8426198 ± 11142'     │ '118 ± 0.20%'          │ '119 ± 0'              │ 60      │
│ 15      │ 'npm.json: diff (cold)'                       │ '19137983 ± 0.11%'  │ '19147548 ± 38419'    │ '52 ± 0.11%'           │ '52 ± 0'               │ 27      │
│ 16      │ 'npm.json: diff (warm)'                       │ '8600679 ± 0.38%'   │ '8560707 ± 25009'     │ '116 ± 0.36%'          │ '117 ± 0'              │ 59      │
│ 17      │ 'npm.json: sync fields (cold)'                │ '44796953 ± 0.27%'  │ '44739515 ± 65844'    │ '22 ± 0.27%'           │ '22 ± 0'               │ 16      │
│ 18      │ 'npm.json: add version'                       │ '37397158 ± 6.26%'  │ '35363940 ± 1252068'  │ '27 ± 5.60%'           │ '28 ± 1'               │ 16      │
│ 19      │ 'npm.json: delete latest version'             │ '29872093 ± 6.48%'  │ '27947414 ± 1057735'  │ '34 ± 5.76%'           │ '36 ± 1'               │ 17      │
│ 20      │ '@primer/react.json: construct'               │ '41577685 ± 3.36%'  │ '40606368 ± 47301'    │ '24 ± 2.92%'           │ '25 ± 0'               │ 16      │
│ 21      │ '@primer/react.json: readme (cold)'           │ '54363397 ± 0.19%'  │ '54315947 ± 51262'    │ '18 ± 0.19%'           │ '18 ± 0'               │ 16      │
│ 22      │ '@primer/react.json: getMetaInfo (cold)'      │ '99688022 ± 1.83%'  │ '99269000 ± 1761526'  │ '10 ± 1.73%'           │ '10 ± 0'               │ 16      │
│ 23      │ '@primer/react.json: getLatestVersion (cold)' │ '68803672 ± 0.40%'  │ '68678163 ± 87456'    │ '15 ± 0.39%'           │ '15 ± 0'               │ 16      │
│ 24      │ '@primer/react.json: getLatestVersion (warm)' │ '28042913 ± 0.08%'  │ '28032521 ± 25838'    │ '36 ± 0.08%'           │ '36 ± 0'               │ 18      │
│ 25      │ '@primer/react.json: diff (cold)'             │ '73825056 ± 0.23%'  │ '73778598 ± 261470'   │ '14 ± 0.23%'           │ '14 ± 0'               │ 16      │
│ 26      │ '@primer/react.json: diff (warm)'             │ '33079997 ± 0.89%'  │ '33066806 ± 234314'   │ '30 ± 0.87%'           │ '30 ± 0'               │ 16      │
│ 27      │ '@primer/react.json: sync fields (cold)'      │ '169825915 ± 0.44%' │ '169675770 ± 910638'  │ '6 ± 0.44%'            │ '6 ± 0'                │ 16      │
│ 28      │ '@primer/react.json: add version'             │ '136812136 ± 0.97%' │ '136486465 ± 2042203' │ '7 ± 0.97%'            │ '7 ± 0'                │ 16      │
│ 29      │ '@primer/react.json: delete latest version'   │ '107796678 ± 1.66%' │ '108446818 ± 2848455' │ '9 ± 1.62%'            │ '9 ± 0'                │ 16      │
└─────────┴───────────────────────────────────────────────┴─────────────────────┴───────────────────────┴────────────────────────┴────────────────────────┴─────────┘

$ node benchmark/bench.ts ⊘ cache disabled
┌─────────┬──────────────────────────────────────────────────────────────────────┬─────────────────────┬───────────────────────┬────────────────────────┬────────────────────────┬─────────┐
│ (index) │ Task name                                                            │ Latency avg (ns)    │ Latency med (ns)      │ Throughput avg (ops/s) │ Throughput med (ops/s) │ Samples │
├─────────┼──────────────────────────────────────────────────────────────────────┼─────────────────────┼───────────────────────┼────────────────────────┼────────────────────────┼─────────┤
│ 0       │ 'JSONParse small data readme string (117KB)'                         │ '466527 ± 0.64%'    │ '430489 ± 19439'      │ '2179 ± 0.50%'         │ '2323 ± 109'           │ 2144    │
│ 1       │ 'sonic-rs small data readme string (117KB)'                          │ '103381 ± 0.32%'    │ '99931 ± 671.00'      │ '9780 ± 0.16%'         │ '10007 ± 67'           │ 9673    │
│ 2       │ 'sonic-rs small data readme string with position (117KB)'            │ '110188 ± 0.29%'    │ '106681 ± 971.00'     │ '9161 ± 0.16%'         │ '9374 ± 85'            │ 9076    │
│ 3       │ 'sonic-rs small data readme JSON buffer with position (117KB)'       │ '86444 ± 0.22%'     │ '85399 ± 221.00'      │ '11603 ± 0.07%'        │ '11710 ± 30'           │ 11569   │
│ 4       │ 'JSONParse large data readme string (22MB)'                          │ '91505246 ± 6.14%'  │ '96799746 ± 23952488' │ '12 ± 6.12%'           │ '11 ± 3'               │ 64      │
│ 5       │ 'sonic-rs large data readme string (22MB)'                           │ '14693362 ± 0.18%'  │ '14685527 ± 48512'    │ '68 ± 0.18%'           │ '68 ± 0'               │ 69      │
│ 6       │ 'sonic-rs large data readme string with position (22MB)'             │ '14745356 ± 0.13%'  │ '14750184 ± 38293'    │ '68 ± 0.13%'           │ '68 ± 0'               │ 68      │
│ 7       │ 'sonic-rs large data readme JSON buffer with position (22MB)'        │ '14765236 ± 0.14%'  │ '14746864 ± 27832'    │ '68 ± 0.14%'           │ '68 ± 0'               │ 68      │
│ 8       │ 'JSONParse super large data readme string (89M)'                     │ '311145721 ± 0.49%' │ '310148431 ± 2066198' │ '3 ± 0.46%'            │ '3 ± 0'                │ 64      │
│ 9       │ 'sonic-rs super large data readme string (89M)'                      │ '54861852 ± 0.61%'  │ '54537894 ± 62463'    │ '18 ± 0.54%'           │ '18 ± 0'               │ 64      │
│ 10      │ 'sonic-rs super large data readme string with position (89M)'        │ '54781518 ± 0.15%'  │ '54690460 ± 73060'    │ '18 ± 0.15%'           │ '18 ± 0'               │ 64      │
│ 11      │ 'sonic-rs super large data readme JSON buffer with position (89M)'   │ '54677086 ± 0.07%'  │ '54639326 ± 62489'    │ '18 ± 0.07%'           │ '18 ± 0'               │ 64      │
│ 12      │ 'JSONParse big readme string (229KB, 64KB readme)'                   │ '615500 ± 3.05%'    │ '584962 ± 8594.0'     │ '1691 ± 0.36%'         │ '1710 ± 25'            │ 1625    │
│ 13      │ 'sonic-rs big readme string (229KB, 64KB readme)'                    │ '197609 ± 0.31%'    │ '194603 ± 902.00'     │ '5078 ± 0.11%'         │ '5139 ± 24'            │ 5061    │
│ 14      │ 'sonic-rs big readme string with position (229KB, 64KB readme)'      │ '224419 ± 0.34%'    │ '219411 ± 1031.0'     │ '4484 ± 0.17%'         │ '4558 ± 21'            │ 4456    │
│ 15      │ 'sonic-rs big readme JSON buffer with position (229KB, 64KB readme)' │ '173100 ± 0.26%'    │ '171148 ± 361.00'     │ '5788 ± 0.07%'         │ '5843 ± 12'            │ 5777    │
│ 16      │ 'JSONParse large data add version (22MB)'                            │ '212579672 ± 0.88%' │ '213475313 ± 863583'  │ '5 ± 0.99%'            │ '5 ± 0'                │ 64      │
│ 17      │ 'sonic-rs large data add version (22MB)'                             │ '35148175 ± 0.76%'  │ '34537101 ± 445516'   │ '28 ± 0.75%'           │ '29 ± 0'               │ 64      │
│ 18      │ 'JSONParse super large data add version (89M)'                       │ '491545420 ± 0.54%' │ '488990859 ± 1890503' │ '2 ± 0.51%'            │ '2 ± 0'                │ 64      │
│ 19      │ 'sonic-rs super large data add version (89M)'                        │ '134523595 ± 0.12%' │ '134486790 ± 308560'  │ '7 ± 0.12%'            │ '7 ± 0'                │ 64      │
└─────────┴──────────────────────────────────────────────────────────────────────┴─────────────────────┴───────────────────────┴────────────────────────┴────────────────────────┴─────────┘

$ node benchmark/bench_real_case.ts ⊘ cache disabled
[small] verify OK (versions=69)
[large] verify OK (versions=573)
[super large] verify OK (versions=12073)
┌─────────┬─────────────────────────────────┬─────────────────────┬───────────────────────┬────────────────────────┬────────────────────────┬─────────┐
│ (index) │ Task name                       │ Latency avg (ns)    │ Latency med (ns)      │ Throughput avg (ops/s) │ Throughput med (ops/s) │ Samples │
├─────────┼─────────────────────────────────┼─────────────────────┼───────────────────────┼────────────────────────┼────────────────────────┼─────────┤
│ 0       │ 'JSON.parse all fields (117KB)' │ '451730 ± 3.04%'    │ '428857 ± 6650.5'     │ '2312 ± 0.28%'         │ '2332 ± 36'            │ 2214    │
│ 1       │ 'sonic-rs multi-get (117KB)'    │ '305329 ± 0.84%'    │ '301054 ± 2704.0'     │ '3295 ± 0.12%'         │ '3322 ± 30'            │ 3276    │
│ 2       │ 'sonic-rs getMetaInfo (117KB)'  │ '177230 ± 0.55%'    │ '174283 ± 1172.0'     │ '5678 ± 0.10%'         │ '5738 ± 39'            │ 5643    │
│ 3       │ 'JSON.parse all fields (22MB)'  │ '90561762 ± 6.03%'  │ '86459893 ± 18378115' │ '12 ± 6.02%'           │ '12 ± 3'               │ 64      │
│ 4       │ 'sonic-rs multi-get (22MB)'     │ '45112640 ± 0.12%'  │ '45082153 ± 58269'    │ '22 ± 0.11%'           │ '22 ± 0'               │ 64      │
│ 5       │ 'sonic-rs getMetaInfo (22MB)'   │ '21259173 ± 0.11%'  │ '21238092 ± 45283'    │ '47 ± 0.11%'           │ '47 ± 0'               │ 64      │
│ 6       │ 'JSON.parse all fields (89MB)'  │ '308948828 ± 0.14%' │ '308604980 ± 738356'  │ '3 ± 0.13%'            │ '3 ± 0'                │ 64      │
│ 7       │ 'sonic-rs multi-get (89MB)'     │ '176818816 ± 0.30%' │ '176065042 ± 643275'  │ '6 ± 0.29%'            │ '6 ± 0'                │ 64      │
│ 8       │ 'sonic-rs getMetaInfo (89MB)'   │ '96721116 ± 0.30%'  │ '96452916 ± 181683'   │ '10 ± 0.29%'           │ '10 ± 0'               │ 64      │
└─────────┴─────────────────────────────────┴─────────────────────┴───────────────────────┴────────────────────────┴────────────────────────┴─────────┘

Memory Usage:
  JSONParse all fields (22M): 371.0 MB (min: 363.4 MB, max: 392.5 MB)
  JSONParse all fields (89M): 588.6 MB (min: 587.8 MB, max: 590.1 MB)
  SonicMultiGet all fields (22M): 96.5 MB (min: 95.5 MB, max: 97.3 MB)
  SonicMultiGet all fields (89M): 203.3 MB (min: 203.2 MB, max: 203.7 MB)
  SonicMetaInfo all fields (22M): 96.8 MB (min: 95.8 MB, max: 97.7 MB)
  SonicMetaInfo all fields (89M): 201.0 MB (min: 199.1 MB, max: 202.3 MB)

$ node benchmark/bench_memory.ts ⊘ cache disabled

Memory Usage:
  JSONParse description (22M): 365.6 MB (min: 364.3 MB, max: 367.1 MB)
  JSONParse description (89M): 587.7 MB (min: 585.4 MB, max: 589.7 MB)
  SonicJSONParse description (22M): 94.5 MB (min: 93.3 MB, max: 95.3 MB)
  SonicJSONParse description (89M): 160.9 MB (min: 160.8 MB, max: 160.9 MB)

Memory Usage:
  JSONParse add version (22M): 400.0 MB (min: 390.3 MB, max: 412.4 MB)
  JSONParse add version (89M): 750.6 MB (min: 747.6 MB, max: 760.6 MB)
  SonicJSONParse add version (22M): 185.4 MB (min: 184.6 MB, max: 186.5 MB)
  SonicJSONParse add version (89M): 430.5 MB (min: 429.3 MB, max: 431.5 MB)

Memory Usage:
  JSONParse get property value (22M): 403.0 MB (min: 401.5 MB, max: 404.2 MB)
  JSONParse get property value (89M): 585.0 MB (min: 582.8 MB, max: 590.6 MB)
  SonicJSONParse get property value (22M): 96.1 MB (min: 95.1 MB, max: 97.1 MB)
  SonicJSONParse get property value (89M): 162.4 MB (min: 162.0 MB, max: 163.6 MB)

$ GC=1 node benchmark/add_version.ts ⊘ cache disabled

## Benchmarking JSONParse with @primer/react.json
- Data size: 89MB
- GC: enabled

JSONParse add version (@primer/react.json) #1 time: 550ms
JSONParse add version (@primer/react.json) #2 time: 532ms
JSONParse add version (@primer/react.json) #3 time: 517ms
JSONParse add version (@primer/react.json) #4 time: 497ms
JSONParse add version (@primer/react.json) #5 time: 498ms

[GC] total(ms)= 207.55 count= 36 avg(ms)= 5.77 byKind= {
  scavenge: 160.8539949997794,
  markSweepCompact: 44.64478600001894,
  incremental: 2.0511039998964407,
  weakc: 0,
  unknown: 0
}

$ GC=1 node benchmark/add_version.ts SonicJSONParse ⊘ cache disabled

## Benchmarking SonicJSONParse with @primer/react.json
- Data size: 89MB
- GC: enabled

SonicJSONParse add version (@primer/react.json) #1 time: 139ms
SonicJSONParse add version (@primer/react.json) #2 time: 137ms
SonicJSONParse add version (@primer/react.json) #3 time: 135ms
SonicJSONParse add version (@primer/react.json) #4 time: 138ms
SonicJSONParse add version (@primer/react.json) #5 time: 135ms

[GC] total(ms)= 4.73 count= 6 avg(ms)= 0.79 byKind= {
  scavenge: 0,
  markSweepCompact: 3.1502259999979287,
  incremental: 1.5754830000223592,
  weakc: 0,
  unknown: 0
}

$ GC=1 node benchmark/get_property_value.ts ⊘ cache disabled

## Benchmarking JSONParse with @primer/react.json
- Data size: 89MB
- GC: enabled

JSONParse get name of @primer/react.json: '@primer/react'
JSONParse get property value (@primer/react.json) #1 time: 358ms
JSONParse get property value (@primer/react.json) #2 time: 346ms
JSONParse get property value (@primer/react.json) #3 time: 337ms
JSONParse get property value (@primer/react.json) #4 time: 313ms
JSONParse get property value (@primer/react.json) #5 time: 316ms

[GC] total(ms)= 129.89 count= 23 avg(ms)= 5.65 byKind= {
  scavenge: 32.21599400008563,
  markSweepCompact: 95.98165999999037,
  incremental: 1.6875430000945926,
  weakc: 0,
  unknown: 0
}

$ GC=1 node benchmark/get_property_value.ts SonicJSONParse ⊘ cache disabled

## Benchmarking SonicJSONParse with @primer/react.json
- Data size: 89MB
- GC: enabled

SonicJSONParse get name of @primer/react.json: '@primer/react'
SonicJSONParse get property value (@primer/react.json) #1 time: 0ms
SonicJSONParse get property value (@primer/react.json) #2 time: 0ms
SonicJSONParse get property value (@primer/react.json) #3 time: 0ms
SonicJSONParse get property value (@primer/react.json) #4 time: 0ms
SonicJSONParse get property value (@primer/react.json) #5 time: 0ms

[GC] total(ms)= 1.79 count= 2 avg(ms)= 0.89 byKind= {
  scavenge: 0,
  markSweepCompact: 1.5792889999574982,
  incremental: 0.20667099999263883,
  weakc: 0,
  unknown: 0
}

---
vp run: 10/10 successful. (Run `vp run --last-details` for full details)

Benchmark ran on x86_64-unknown-linux-gnu target

@codspeed

codspeed Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will improve performance by 21.85%

⚠️ 1 benchmark spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

⚡ 4 improved benchmarks
✅ 25 untouched benchmarks
🆕 30 new benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
⚡ sonic-rs multi-get (22MB) 659.4 ms 492.1 ms +34%
⚡ sonic-rs large data add version (22MB) 514.7 ms 428.7 ms +20.07%
⚡ sonic-rs super large data add version (89M) 1.8 s 1.5 s +17.15%
⚡ sonic-rs multi-get (89MB) 2.5 s 2.1 s +16.94%
🆕 @primer/react.json: add version N/A 1.7 s N/A
🆕 @primer/react.json: construct N/A 515.4 ms N/A
🆕 @primer/react.json: delete latest version N/A 1.5 s N/A
🆕 @primer/react.json: diff (cold) N/A 880.1 ms N/A
🆕 @primer/react.json: diff (warm) N/A 364.4 ms N/A
🆕 @primer/react.json: getLatestVersion (cold) N/A 839.4 ms N/A
🆕 @primer/react.json: getLatestVersion (warm) N/A 323.7 ms N/A
🆕 @primer/react.json: getMetaInfo (cold) N/A 1.1 s N/A
🆕 @primer/react.json: readme (cold) N/A 676.3 ms N/A
🆕 @primer/react.json: sync fields (cold) N/A 1.9 s N/A
🆕 a.json: add version N/A 2.2 ms N/A
🆕 a.json: construct N/A 880.5 µs N/A
🆕 a.json: delete latest version N/A 2 ms N/A
🆕 a.json: diff (cold) N/A 1.2 ms N/A
🆕 ⚠️ a.json: diff (warm) N/A 1.2 ms N/A
🆕 a.json: getLatestVersion (cold) N/A 1.2 ms N/A
... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing perf/reuse-validated-json (4fe81af) with master (a3b8402)

Open in CodSpeed

@fengmk2
fengmk2 merged commit 0cb344b into master Oct 17, 2026
33 checks passed
@fengmk2
fengmk2 deleted the perf/reuse-validated-json branch October 17, 2026 07:28
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.

2 participants