Exp 235: resume SWAR escape scan after each dirty chunk in json_write_string - #254
Open
danReynolds wants to merge 1 commit into
Open
Exp 235: resume SWAR escape scan after each dirty chunk in json_write_string#254danReynolds wants to merge 1 commit into
danReynolds wants to merge 1 commit into
Conversation
…_string The selectBytes TEXT escaper's SWAR 8-byte fast-skip broke to the byte-by-byte path on the first escapable byte and never resumed, so a single early escape in a long value dragged the whole tail onto the slow path. Re-enter the SWAR skip after each dirty chunk: a sparse escape now only downgrades the one 8-byte chunk that contains it. Two order-flipped focused rounds: -23.4% on the load-bearing 256B sparse-newline lane (realistic multi-line text), -31% to -44% on the sparse 1KiB/256B lanes, with the escape-free common path byte-identical (parity). Cost is +6.0% on pathological dense-escape 256B text only (one extra SWAR probe per all-dirty chunk). Pure portable C; no API/platform change; output byte-identical (dart:convert oracle unchanged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
danReynolds
force-pushed
the
exp-235-json-escape-swar-resume
branch
from
July 21, 2026 14:10
8f95888 to
0a352ed
Compare
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.
Hypothesis
While reading the
selectBytesTEXT path I noticedresqlite_json_write_stringscans each TEXT cell for JSON-escapable bytes with a SWAR fast-skip that inspects
8 bytes at a time and skips whole clean chunks — the common case, text with no
escapes. But the loop was written as
while (i + 8 <= len) { ... break; }: itbreaks to a trailing byte-by-byte loop the moment it sees the firstescapable byte, and never returns to the fast skip.
So a single escape early in an otherwise-safe value forces the entire remaining
tail through the one-byte-at-a-time path. A 256-byte log line with a newline near
the front, a multi-line chat message, a paragraph with a stray quote, JSON stored
as TEXT — all are >95% safe bytes but get crawled one byte at a time because the
scanner saw one escape early. I expected that re-entering the SWAR skip after the
escape would recover the fast path on the safe spans, winning on this (common)
sparse-escape shape while leaving the escape-free path untouched.
Approach
The scanner becomes an outer loop wrapping the original tight SWAR skip. The
inner
while (i + 8 <= len)skip is kept verbatim (one bounds check per cleanchunk); on a miss it processes exactly the one offending 8-byte chunk (or the
final <8-byte tail) byte-by-byte, then the outer loop resumes the SWAR skip. A
sparse escape therefore only downgrades the single chunk that contains it; the
clean chunks on either side keep skipping 8 at a time.
Deliberately conservative: escape emission, the lookup tables, and
\uXXXXhandling are unchanged, so output is byte-identical. Two design choices are
load-bearing for the guards — keeping the tight inner SWAR loop (an earlier
single-
whilestructure with a doubled bounds check regressed the safe/lateguards +7%), and re-probing SWAR once per dirty chunk rather than per byte (a
first version re-probed per safe byte and regressed dense-escape +24%). Full
detail in experiments/235-json-escape-swar-resume.md.
Results
Two rounds of order-flipped focused passes (
select_bytes_text_string_reserve.dart),Apple Silicon. The load-bearing acceptance row (declared before measuring) is the
256B sparse-newline lane — realistic multi-line text with a
\nevery ~80 bytes.Realistic multi-line TEXT encodes ~1.3×–1.8× faster, and the one-early-escape
extreme is nearly 2× faster, because the old scanner turned a single early escape
into a full-length byte crawl and the new one does not. The escape-free common
path (the dominant real workload) is byte-identical and measures as parity. The
only real cost is dense-escape 256B at +6.0% — text where ~⅓ of bytes need
escaping pays one extra SWAR probe per all-dirty chunk; that distribution is not
natural text and is not in the release suite, so the realistic sparse win
dominates.
Outcome
Accepted (in review). A contained ~−23% (load-bearing) to −44% win on
sparse-escape TEXT
selectBytesencoding — the common shape of multi-line andlightly-quoted text — with the escape-free path provably unchanged and output
byte-identical. Cost is ~+6% on pathological dense-escape 256B only. No API,
platform, or allocation change; pure portable C. Would reopen the dense
regression only if a workload/profile shows dense-escape TEXT is a hot path.
Test plan
dart test test/native_encoder_diff_test.dart— 7/7 pass, including thebyte-exact
dart:convertoracle that injects escapes at positions{0, 15, 16, len−1} across lengths 0–1024 (directly covers the resume path)
dart test test/select_bytes_transfer_test.dartanddart test test/database_test.dart -N selectBytes— passdart analyzeon changed sources — cleandart run benchmark/finalize_experiment.dart --experiment=experiments/235-json-escape-swar-resume.md— green