Skip to content

perf(arrays): defer index string conversion - #75

Merged
BridgeAR merged 1 commit into
mainfrom
BridgeAR/2026-10-12-stringify-perf
Oct 13, 2026
Merged

BridgeAR merged 1 commit into
mainfrom
BridgeAR/2026-10-12-stringify-perf

Conversation

@BridgeAR

Copy link
Copy Markdown
Owner

Array indexes only need strings when they reach toJSON or function replacer callbacks.

A four-number array uses 86 ns instead of 107 ns, a 20% CPU reduction on Node 26.3.1 (V8 14.6.202.34-node.20).
Measurements use 50,000 warmup calls and seven alternating trials, with the fastest and slowest trials removed.

Drive-by fix: Mark intentional invalid-option assertions for TypeScript.

Array indexes only need strings when they reach toJSON or function replacer
callbacks. A four-number array uses 86 ns instead of 107 ns on Node 26.3.1
(V8 14.6.202.34-node.20), a 20% CPU reduction. Measurements use 50,000 warmup
calls and seven alternating trials, with the fastest and slowest removed.

Drive-by fix: Mark intentional invalid-option assertions for TypeScript.
@BridgeAR
BridgeAR marked this pull request as ready for review October 13, 2026 05:13
@BridgeAR
BridgeAR merged commit e8e5b3b into main Oct 13, 2026
5 checks passed
@BridgeAR
BridgeAR deleted the BridgeAR/2026-10-12-stringify-perf branch October 13, 2026 05:14
kitschpatrol pushed a commit to kitschpatrol/safe-stable-stringify that referenced this pull request Nov 2, 2026
* fix: do not throw on a negative number `space` (BridgeAR#65)

`JSON.stringify` treats any numeric `space` below 1 as no indentation, but
`stringify` threw on it:

    JSON.stringify({ a: 1 }, null, -1) // '{"a":1}'
    stringify({ a: 1 }, null, -1)      // RangeError: Invalid count value: -1

The README documents the spacer as behaving "the same as with
JSON.stringify()". `space` was clamped to a maximum of 10 but not a minimum
of 0, so a negative count reached `String.prototype.repeat`, which throws
for counts <= -1. Clamp the lower bound to 0.

* Fix merge conflict in readme (BridgeAR#63)

Looks like this was accidentally introduced in BridgeAR@132e76c

* fix: enforce maximumDepth for nested objects with array replacers (BridgeAR#64)

* feat(circular)!: include ancestor depth in default markers (BridgeAR#70)

Circular references to different ancestors currently produce the same marker, which hides where each cycle closes. The new marker identifies the referenced ancestor's depth, starting at 0 for the root.

BREAKING CHANGE: The default marker changes from `[Circular]` to `[Circular: N]`. Set `circularValue: '[Circular]'` to retain the previous output.

* fix(typed-array): serialize non-finite floating-point values (BridgeAR#73)

* Serialize non-finite typed-array values consistently

Fixes: BridgeAR#68

* perf(typed-array): inline floating-point checks

A formatter closure adds finite checks to integer arrays and an extra call for floating-point values. Inline checks retain direct conversion for integer and BigInt arrays.

---------

Co-authored-by: Ferit <frtcm11@gmail.com>

* docs(circular): clarify JSON escaping for custom markers (BridgeAR#74)

* perf(arrays): defer index string conversion (BridgeAR#75)

Array indexes only need strings when they reach toJSON or function replacer
callbacks. A four-number array uses 86 ns instead of 107 ns on Node 26.3.1
(V8 14.6.202.34-node.20), a 20% CPU reduction. Measurements use 50,000 warmup
calls and seven alternating trials, with the fastest and slowest removed.

Drive-by fix: Mark intentional invalid-option assertions for TypeScript.

* perf(sort): bound insertion sort work (BridgeAR#76)

Disordered objects with up to 200 keys can spend most of their serialization
time shifting keys. A shuffled 200-key object uses 16.6 us instead of 45.6 us
on Node 26.3.1 (V8 14.6.202.34-node.20), a 64% CPU reduction. Measurements
use 50,000 warmup calls and seven alternating trials, with the fastest and
slowest removed. Nearly ordered keys retain the insertion path.

Drive-by fix: Mark intentional invalid-option assertions for TypeScript.

* feat(modules)!: share ESM and CommonJS exports (BridgeAR#72)

The implementation is cached after the first load, so removing its global
export leaves the other wrapper without an export. Retain a version-specific
symbol so ESM imports, CommonJS requires, and independent copies of one
version receive the same function.

BREAKING CHANGE: Require Node.js 18 or later.

* perf: reduce primitive serialization overhead (BridgeAR#77)

Primitive array elements and object properties enter recursive serializers
even though they need no traversal. Share primitive formatting across
serialization modes while preserving toJSON ordering and per-value error
handling in safe mode.

Native string escaping becomes faster at shorter string lengths on
Node.js 22 and later. Select the cutoff once at module load so older
runtimes retain their existing fast path.

---------

Co-authored-by: Spoko Dev <spoko.dev@gmail.com>
Co-authored-by: Andy Trevorah <andy@trevorah.net>
Co-authored-by: Chamath Adithya <achamath1@gmail.com>
Co-authored-by: Ruben Bridgewater <ruben@bridgewater.de>
Co-authored-by: Ferit <frtcm11@gmail.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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