perf: SIMD-accelerate JSON string paths (from_json/to_json) - #13
Merged
Conversation
Add 128-bit SIMD (ARM NEON + x86 SSE2, scalar fallback) to the two byte-at-a-time string loops in the msgpack<->JSON conversion, the one place in the extension where raw byte throughput dominates: - mpJpParseString (from_json): SIMD scan for '"'/'\' then bulk-copy the safe run in a single append. - mpJsonEscapeStr (to_json/pretty): SIMD scan for '"'/'\'/control bytes then jump straight to the next byte needing escaping. New helpers mpScanStr/mpScanEsc use full 16-byte loads only (no out-of-bounds over-read), a scalar tail, and only the always-available baseline ISA (no runtime CPU dispatch). A cheap first-byte guard avoids any regression on escape-dense input. Define MSGPACK_DISABLE_SIMD to force the portable scalar path. MessagePack navigation (extract/set/remove/type) is a serial length-prefixed pointer-chase that cannot be vectorized, and key/buffer ops already use libc SIMD memcmp/memcpy, so those are left untouched. Measured on arm64/NEON, ~4 KB strings (A/B vs MSGPACK_DISABLE_SIMD): - from_json, no escapes: ~2670 -> ~579 ns/op (~4.6x) - to_json, no escapes: ~2590 -> ~510 ns/op (~5.0x) - to_json, all escapes (worst case): parity, no regression Small strings are unchanged (bound by SQLite call overhead). Correctness: 16/16 ctest pass; a 6000-case deterministic differential fuzz and 15 boundary edge cases (escapes at 16-byte chunk offsets, control bytes, tails) produce byte-identical output to the scalar path. SSE2 branch cross-compiles clean and is MSVC-safe (_BitScanForward). Also adds large-string rows to bench_msgpack_vs_json.c. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds 128-bit SIMD (ARM NEON + x86 SSE2, with a scalar fallback) to the two byte-at-a-time string loops in the msgpack⇄JSON conversion — the one place in this extension where raw byte throughput actually dominates.
mpJpParseString(msgpack_from_json): SIMD-scan for"/\, then bulk-copy the safe run in a single append.mpJsonEscapeStr(msgpack_to_json/msgpack_pretty): SIMD-scan for"/\/control bytes, then jump straight to the next byte that needs escaping.New helpers
mpScanStr/mpScanEsc:i+16<=len) so we never read past the buffer; the short tail is handled scalar.-DMSGPACK_DISABLE_SIMDforces the portable scalar path.Why not
extract/set?MessagePack is a length-prefixed TLV format, so element navigation (
mpSkipOne/mpLookup) is a serial pointer-chase that can't be vectorized; key/buffer ops already use libc SIMDmemcmp/memcpy. Those paths are intentionally left untouched.Measured gain
arm64/NEON, Release, ~4 KB strings, A/B vs
MSGPACK_DISABLE_SIMD:from_json, no escapesto_json, no escapesto_json, all-escapes (worst)Small strings are unchanged (bound by SQLite call overhead) — as expected.
Correctness
ctestpass; fuzz corpus pass._BitScanForward).Changes
src/msgpack.c— SIMD scanners + wired into the two JSON string loops.tests/bench_msgpack_vs_json.c— large-string benchmark rows (plain + all-escapes).