Skip to content

Generate schema-specialized binary writers - #16

Merged
jallum merged 1 commit into
perf/generated-readerfrom
perf/generated-writer
Aug 31, 2026
Merged

Generate schema-specialized binary writers#16
jallum merged 1 commit into
perf/generated-readerfrom
perf/generated-writer

Conversation

@jallum

@jallum jallum commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Stacked on #14 (and #13).

Summary

  • generate schema-specialized reverse-builder dispatch during use Flatbuffer expansion
  • keep schema-independent mechanics shared in the internal Flatbuffer.Writer: final framing, string pooling, vector materialization, scalar validation, aligned pushes, and vtable construction
  • compile table fields and IDs, scalar defaults, enum/union dispatch, struct padding, vector element sizes, and alignment into the caller
  • precompute alignment-friendly table emission order while independently assembling vtable locations in field-ID order
  • preserve to_iolist/1 and to_binary/1, compact/shared vtables, shared strings, atom/binary input keys, safe schemas, recursive tables, and existing error tuples
  • extend flatc interoperability so the C++ verifier reads buffers from both interpreted and generated writers

The shared-runtime refactor removed 291 lines (about 30%) from the writer generator; generated code now contains only the schema-specialized control flow and measurements.

TDD and verification

The first test traced Flatbuffer.to_iolist/2 and Flatbuffer.to_binary/2; it failed on the original macro delegation and passes only when both calls use generated code.

  • Elixir 1.18 / OTP 27: 138 tests, 0 failures, 2 excluded
  • Elixir 1.19 / OTP 28: 136 tests, 0 failures, 2 excluded
  • Elixir 1.20 / OTP 29 coverage: 136 tests, 0 failures, 99.5%
  • flatc interoperability: 2 tests, 0 failures; C++ verifies interpreted and generated writer buffers
  • mix deps.unlock --check-unused
  • mix compile --warnings-as-errors
  • mix format --check-formatted
  • git diff --check

Benchee

M4 Pro, Elixir 1.19.4, OTP 28.3, 100 integers and 20 child tables, after shared-runtime extraction:

writer average median memory reductions
interpreted 11.45 µs 11.21 µs 55.45 KB 4.62 K
generated 10.30 µs 10.08 µs 45.80 KB 3.56 K

The generated writer remains about 10% faster, allocates 17.4% less memory, and uses 22.9% fewer reductions. Both produce a 1,384-byte representative buffer.

flatc review and alignment ordering

flatc generates fixed add_* calls and groups scalar emission by wire size/alignment. The Elixir generator follows the same useful property, but computes true schema alignment for structs as well. Reference objects are prepared first; inline fields are emitted by descending alignment with a deterministic field-ID tiebreaker. Each emitted location has its own generated local, so the vtable is assembled in ID order without a runtime sort. This is why alignment-friendly ordering is practical in either implementation but only wins here after the ordering work itself is moved to compile time.

Adversarial review

  • alternate valid field order changes non-canonical bytes, so tests compare decoded semantics and flatc verification rather than assuming one byte layout
  • sparse/absent fields still produce compact vtables because generated location collection is ID-ordered and stops at the highest present field
  • union vectors retain the existing explicit error behavior
  • safe-schema code generation never interns schema field names; hygienic locals avoid field-number-derived atoms
  • shared strings/vtables, default omission, nested structs, recursive tables, malformed scalar/enum/table/union values, binary keys, and iolist output have dedicated parity coverage
  • the module boundary was benchmarked after extraction; only schema-dependent dispatch remains quoted

@jallum
jallum force-pushed the perf/generated-writer branch from 0475f6d to b6c0e4f Compare August 31, 2026 20:07
@jallum
jallum merged commit 14e141a into main Aug 31, 2026
4 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.

1 participant