Skip to content

[Perf][JSON] Improve string escaping in JSONWriter - #3469

Merged
rintaro merged 1 commit into
swiftlang:mainfrom
rintaro:json-encode-string-escaping
Oct 10, 2026
Merged

rintaro merged 1 commit into
swiftlang:mainfrom
rintaro:json-encode-string-escaping

Conversation

@rintaro

@rintaro rintaro commented Oct 8, 2026

Copy link
Copy Markdown
Member

JSONWriter.serialize(string:) checked one byte at a time and made one or two small append(contentsOf:) calls for every escaped byte. That's slow for strings with many escapes, like a JSON document embedded as a string value, and the byte-by-byte scan also slowed down strings with nothing to escape.

This PR encodes each string in two passes:

  • Count: using SIMD16<UInt8>, count the bytes that need escaping (", \, and control characters) and how many of them are control characters. The compiler didn't vectorize the plain loop on its own.
  • Write, nothing to escape: append the whole UTF-8 buffer at once.
  • Write, otherwise: grow the output once to the worst-case size and write through a pointer. Chunks of 16 bytes with nothing to escape are stored directly; chunks that contain an escape, and the tail, are written byte by byte. The unused space is trimmed at the end.

Measured with a standalone -O -wmo benchmark built from the encoder sources (arm64, macOS):

case before after
embedded JSON (216 KB output) ~480 µs ~165 µs
Swift source (140 KB, \n every ~70 bytes) ~105 µs ~43 µs
non-ASCII text, nothing to escape (94 KB) ~55 µs ~6.3 µs
5000 small objects ~2.34 ms ~2.31 ms

Count the bytes that need escaping 16 bytes at a time with SIMD. If
there are none, append the whole string at once. Otherwise, grow the
output once to the upper bound, copy escape-free 16 byte chunks
directly, and write escapes in place, instead of appending small runs
for each escape.

Encoding a string that embeds JSON is about 2.9x faster, source text
about 2.4x, and strings without escapes about 9x.
@rintaro

rintaro commented Oct 8, 2026

Copy link
Copy Markdown
Member Author

@swift-ci Please test

1 similar comment
@rintaro

rintaro commented Oct 10, 2026

Copy link
Copy Markdown
Member Author

@swift-ci Please test

@rintaro
rintaro merged commit d72cf14 into swiftlang:main Oct 10, 2026
42 checks passed
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