Skip to content

Tags: codeinred/simdjson

Tags

4.6.3-nan-inf

Toggle 4.6.3-nan-inf's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
builder: position-as-local writer for reflection serializer (simdjson…

…#2708)

The dominant cost in the reflection serializer was the strict-aliasing
reload pattern: every char* write through `string_builder::buffer.get()`
forced the compiler to assume `string_builder::position` and `::capacity`
may have been clobbered, so it reloaded both members from memory after
each byte. perf annotate showed `ldr [position]` and `ldr [capacity]`
taking 15-20% of CITM serialization time.

This change refactors the reflection atom path to use an internal
`writer` struct (buffer pointer + position + capacity + back-ref to
string_builder). The writer lives as a stack-local in the top-level
append() entry point, threaded through the inlined atom() chain via
`writer&`. After SROA, the three fields are register-resident across
the entire serialization. The `pos` is never round-tripped through
memory between writes — it's a local size_t.

This mirrors the pattern Glaze uses (passing `B&& b, auto&& ix` through
every helper). The simdjson-specific bit is keeping the public
string_builder API intact: only the internal atom() family is
refactored to take `writer&`. The top-level append(string_builder&, T)
constructs a writer on the stack, threads it through atom(), then
syncs the local position back at the end.

For string fields specifically, the escape path is also inlined
through the writer — the existing `write_string_escaped(input, out)`
helper already takes a destination pointer and returns bytes-written,
so it composes cleanly with `w.ptr + w.pos`. Without this, sync/
reload around each string write was a real cost on Twitter (-7%).

Performance
===========

TRUE A/B in single docker invocation, 7 alternating rounds, byte-
identical output, all static_reflection_comprehensive_tests pass.
Build: clang-p2996 21.0.0git, -O3 -DNDEBUG, aarch64 Linux.

CITM Catalog (496 682 bytes):
  baseline (master): 4356 MB/s
  patched:           6776 MB/s   +55.5%
  Glaze:             4860 MB/s   simdjson now 39.4% AHEAD of Glaze

Twitter (81 927 bytes):
  baseline (master): 6437 MB/s
  patched:           6452 MB/s   +0.2% (break-even)
  Twitter is string-heavy; the writer's gain over the existing
  member-based path is small here, but it no longer regresses.

Default initial capacity is unchanged at 1024.

4.6.3-42c047a9

Toggle 4.6.3-42c047a9's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
builder: position-as-local writer for reflection serializer (simdjson…

…#2708)

The dominant cost in the reflection serializer was the strict-aliasing
reload pattern: every char* write through `string_builder::buffer.get()`
forced the compiler to assume `string_builder::position` and `::capacity`
may have been clobbered, so it reloaded both members from memory after
each byte. perf annotate showed `ldr [position]` and `ldr [capacity]`
taking 15-20% of CITM serialization time.

This change refactors the reflection atom path to use an internal
`writer` struct (buffer pointer + position + capacity + back-ref to
string_builder). The writer lives as a stack-local in the top-level
append() entry point, threaded through the inlined atom() chain via
`writer&`. After SROA, the three fields are register-resident across
the entire serialization. The `pos` is never round-tripped through
memory between writes — it's a local size_t.

This mirrors the pattern Glaze uses (passing `B&& b, auto&& ix` through
every helper). The simdjson-specific bit is keeping the public
string_builder API intact: only the internal atom() family is
refactored to take `writer&`. The top-level append(string_builder&, T)
constructs a writer on the stack, threads it through atom(), then
syncs the local position back at the end.

For string fields specifically, the escape path is also inlined
through the writer — the existing `write_string_escaped(input, out)`
helper already takes a destination pointer and returns bytes-written,
so it composes cleanly with `w.ptr + w.pos`. Without this, sync/
reload around each string write was a real cost on Twitter (-7%).

Performance
===========

TRUE A/B in single docker invocation, 7 alternating rounds, byte-
identical output, all static_reflection_comprehensive_tests pass.
Build: clang-p2996 21.0.0git, -O3 -DNDEBUG, aarch64 Linux.

CITM Catalog (496 682 bytes):
  baseline (master): 4356 MB/s
  patched:           6776 MB/s   +55.5%
  Glaze:             4860 MB/s   simdjson now 39.4% AHEAD of Glaze

Twitter (81 927 bytes):
  baseline (master): 6437 MB/s
  patched:           6452 MB/s   +0.2% (break-even)
  Twitter is string-heavy; the writer's gain over the existing
  member-based path is small here, but it no longer regresses.

Default initial capacity is unchanged at 1024.

v4.6.4

Toggle v4.6.4's commit message
release 4.6.4

v4.6.3

Toggle v4.6.3's commit message
patch 4.6.3

v4.6.2

Toggle v4.6.2's commit message
version bump.

v4.6.1

Toggle v4.6.1's commit message
4.6.1

v4.6.0

Toggle v4.6.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
4.6.0 (simdjson#2655)

v4.5.0

Toggle v4.5.0's commit message
4.5.0

v4.4.2

Toggle v4.4.2's commit message
4.4.2