Skip to content

Zig

Zig is in this suite because comptime reflection (@typeInfo) is a different implementation model from Java/Kotlin runtime reflection, C# source generation, or Rust derive macros. The runner times official std.json (typed parseFromSlice versus streaming Scanner / parseFromTokenSource) against an in-tree comptime byte-packed baseline, against serde.zig, a format-agnostic framework that uses the same @typeInfo walk for JSON, MessagePack, YAML, TOML, and ZON, against typed third-party codecs that specialize the whole encoder into the destination type (json.zig, jsonz, msgpack.zig), and against schema codecs (Protocol Buffers, FlatBuffers, Cap’n Proto) generated from the shared suite IDLs.

Runtime

What it is

Zig compiles to native machine code. There is no hidden virtual machine and no garbage collector. Allocation is explicit: the benchmark runner passes allocators in. Comptime means the compiler can run Zig code while it builds. @typeInfo walks a struct at compile time instead of using Java-style reflection at run time.

This suite
Compiler Zig 0.16.x. Version 0.15 or 0.17 will not build this tree.
Prepare ./scripts/install-host-requirements.sh zig installs into ~/.local/zig
Run zig/scripts/run-benchmarks.sh (zig build)
Memory Explicit allocators. No garbage collector.

What this suite runs

Zig still changes in breaking ways between minor versions, so the host script installs 0.16.x and the checker rejects any other series. The Zig binary does not start Python. Cap’n Proto uses the official C++ library, because Zig 0.16 has no native Cap’n Proto plugin.

What changes the numbers

Building without optimizations is the error that changes the numbers the most, because an unoptimized Zig binary is far slower than a release build. Codecs that use comptime, such as std.json, serde.zig, and comptime-bin, generate the field walk while the program compiles.

A @bitCast of a live suite value is not a valid encoding. Slices inside that value are pointers, not payload bytes. See Not a @bitCast.

Suite-specific gotchas

The capnproto row needs libcapnp and libkj under ~/.local.

serde.yaml skips document and event: serde.zig 1.2.2 loses the fields of a struct nested in a list, so those cells would be a timing for a decode that did not restore the value. zig/src/serde_ser.zig has the round-trip test that pins exactly which shapes fail and with which error.

These times cannot be ranked against another language.

Where to go next

The steps to install the toolchain and run the benchmark are in zig/README.md. The language overview is Zig overview.

Benchmark runner

  • Directory: zig/ (repository root)
  • Output: logs/zig/YYYY-MM-DD-HHMMSS.csv (Language=zig, times in nanoseconds)
  • Runner: zig/scripts/run-benchmarks.sh {smoke|all-single|full|research}
  • Registration: zig/src/serializers.zig

The shell script resolves the run config to JSON. The Zig binary does not spawn Python. Prepare is untimed. The harness owns a reusable output buffer per serializer, and every text codec writes into it through std.Io.Writer, so no row pays for a throwaway slice the runner then copies and frees. Timed I/O is serialize plus deserialize only. Schedule is SHA-256 + SplitMix64 Fisher–Yates (golden vector C, B, A).

Serializers (wired)

Name Category Package Stream Notes
std.json JSON std text_on_stream Stringify.value + parseFromSlice into the suite struct
std.json.borrowed JSON std text_on_stream Same stringify; decode is parseFromSliceLeaky with alloc_if_needed
std.json.scanner JSON std text_on_stream Same stringify; decode is Scanner + parseFromTokenSource
std.zon ZON std text_on_stream Official std.zon.stringify + std.zon.parse.fromSliceAlloc
comptime-bin Binary in-tree adapted Comptime field walk; LE ints; u32 length + bytes for strings
serde.json JSON serde.zig 1.2.2 text_on_stream One comptime API, JSON path
serde.json.borrowed JSON serde.zig 1.2.2 text_on_stream Same encode; decode is fromSliceBorrowed
serde.msgpack Binary serde.zig 1.2.2 text_on_stream Same API, MessagePack
serde.yaml Text serde.zig 1.2.2 text_on_stream Same API, YAML. no document / event
serde.toml Text serde.zig 1.2.2 text_on_stream Same API, TOML
serde.zon Text serde.zig 1.2.2 text_on_stream Same API, Zig Object Notation
serde.xml Text serde.zig 1.2.2 text_on_stream Same API, XML
zig-msgpack Binary zigcc/zig-msgpack 0.0.18 adapted Official MessagePack Payload API
msgpack.zig Binary lalinsky/msgpack.zig 0.9.0 native Typed encode / decodeFromSlice
json.zig JSON lalinsky/json.zig 0.1.0 native Typed encode / decodeFromSliceLeaky into the cell arena
jsonz JSON KercyDing/jsonz 0.9.2 text_on_stream typed.toWriter into the reused buffer / owning typed.parse; strings are copied
zbor Binary r4gus/zbor 0.21.3 adapted Native Zig CBOR (stringify / parse)
s2s Binary ziglibs/s2s native Native binary “struct to stream”
protobuf Schema Arwalk/zig-protobuf 5.0.0 adapted Generated from schemas/v2/protobuf/benchmark_v2.proto. Prepare copies suite → generated message; timed path is encode / decode
flatbuffers Schema nDimensional/zig-flatbuffers 0.2.1 adapted Generated from cpp/schemas/benchmark.fbs. Timed path is Builder.writeTable / decodeRoot
capnproto Schema Cap’n Proto C++ 1.0.2 adapted Generated from cpp/schemas/benchmark.capnp. Official C++ runtime via a C ABI (same pattern as Swift). Zig 0.16 has no native plugin
gld-avro Schema leo-gan/gld-avro-zig 0.2.0 adapted parseAvsc in prepare; timed Datum.encodeTo / decodeBin
gld-arrow Schema leo-gan/gld-arrow-zig 0.2.0 adapted Arrow IPC stream for the five suite types; rowsFromBatch on decode
gld-kiln Binary leo-gan/gld-kiln-zig 0.2.0 adapted Positional schema parsed once; timed encodeReuse / decode. N=1 is the struct; N>1 is one list

Specifics

Why each library exists, what problem it was written to solve, and how. Names link to the source repository (or the stdlib / in-tree path this suite times). A version after the name is the last measured SerializerVersion from this suite's latest bench.

std.json · 0.16.0

Zig's std.json is the standard-library JSON codec. It exists so Zig programs can speak JSON without a package. This suite times typed parseFromSlice and, separately, the Scanner path.

std.json.scanner · 0.16.0

Zig's std.json is the standard-library JSON codec. It exists so Zig programs can speak JSON without a package. This suite times typed parseFromSlice and, separately, the Scanner path. Decode is Scanner + parseFromTokenSource; stringify is the same as std.json.

std.zon · 0.16.0

ZON (Zig Object Notation) is Zig's own data notation, in the standard library. It exists as a Zig-native text format for config and data. This row times official stringify/parse.

comptime-bin · in-tree

comptime-bin is the suite's in-tree Zig baseline: a comptime @typeInfo walk that writes little-endian, length-prefixed fields. It exists because @bitCast of a live fixture is not a valid encoding (slices are pointers).

serde.json · 1.2.1

serde.zig is a format-agnostic serialization framework for Zig that walks types with @typeInfo at comptime. The problem was writing a new field walk per format. One API covers JSON, MessagePack, YAML, TOML, ZON, and XML. This row is the JSON backend of serde.zig.

serde.msgpack · 1.2.1

serde.zig is a format-agnostic serialization framework for Zig that walks types with @typeInfo at comptime. The problem was writing a new field walk per format. One API covers JSON, MessagePack, YAML, TOML, ZON, and XML. This row is the MessagePack backend of serde.zig.

serde.yaml · 1.2.1

serde.zig is a format-agnostic serialization framework for Zig that walks types with @typeInfo at comptime. The problem was writing a new field walk per format. One API covers JSON, MessagePack, YAML, TOML, ZON, and XML. This row is the YAML backend of serde.zig (message / strings only).

serde.toml · 1.2.1

serde.zig is a format-agnostic serialization framework for Zig that walks types with @typeInfo at comptime. The problem was writing a new field walk per format. One API covers JSON, MessagePack, YAML, TOML, ZON, and XML. This row is the TOML backend of serde.zig.

serde.zon · 1.2.1

serde.zig is a format-agnostic serialization framework for Zig that walks types with @typeInfo at comptime. The problem was writing a new field walk per format. One API covers JSON, MessagePack, YAML, TOML, ZON, and XML. This row is the ZON backend of serde.zig.

serde.xml · 1.2.1

serde.zig is a format-agnostic serialization framework for Zig that walks types with @typeInfo at comptime. The problem was writing a new field walk per format. One API covers JSON, MessagePack, YAML, TOML, ZON, and XML. This row is the XML backend of serde.zig (message / strings only).

zig-msgpack · 0.0.18

zigcc/zig-msgpack is a MessagePack implementation for Zig. MessagePack exists as compact binary JSON. This package exposes a Payload encode/decode API.

msgpack.zig · 0.9.0

lalinsky/msgpack.zig is another MessagePack library for Zig with a typed encode/decode API. It exists as a native Zig implementation of the same MessagePack spec.

json.zig

lalinsky/json.zig is a typed JSON library for Zig: the encoder and decoder are comptime-specialized into the Zig type, so there is no DOM and no runtime schema. It exists to make JSON cheap for APIs with a fixed schema, and it reads and writes std.Io readers and writers, so a value larger than the buffer still decodes.

jsonz · 0.9.2

jsonz is a native Zig JSON library with typed and DOM APIs. This row uses typed.toWriter into the reused output buffer and typed.parse into suite structs. Strings are copied; its pool and fallback allocations remain in the trial arena until fidelity checking. Allocation is timed; arena cleanup is outside decode timing.

zbor · 0.21.3

zbor is a native Zig CBOR library. CBOR is the IETF binary JSON-like format. zbor implements stringify/parse for Zig types.

s2s · 0.0.1

s2s (struct to stream) is a Zig-only binary encoder that writes structs to a stream. It was created as a simple native binary path, not a public interchange standard.

protobuf · 5.0.0

Arwalk/zig-protobuf generates Zig from .proto files. Protocol Buffers exist as a language-neutral IDL. This package is the Zig implementation this suite uses.

flatbuffers · 0.2.1

nDimensional/zig-flatbuffers generates Zig from FlatBuffers schemas. FlatBuffers exists so readers can use data without unpacking. This is the Zig codegen this suite times.

capnproto · 1.0.2

Cap'n Proto was created by Kenton Varda (after protobuf 2) so RPC and storage could use a binary layout that is already the in-memory representation — no encode step. The problem was protobuf's parse/serialize cost. Cap'n Proto solves it with an IDL and packed/unpacked segments.

gld-avro · 0.2.0

gld-avro-zig is a from-scratch Apache Avro library for Zig. Avro exists for compact, schema-driven records. Prepare parses the schema and builds a Datum. The timed path is Datum.encodeTo and decodeBin.

gld-arrow · 0.2.0

gld-arrow-zig is a from-scratch Apache Arrow library for Zig. This suite row encodes one suite value as an Arrow IPC stream and reads it back with rowsFromBatch. It is not the columnar data set.

gld-kiln · 0.2.0

gld-kiln-zig is a from-scratch Kiln codec for Zig. Kiln is a positional little-endian layout: field order is the wire order, and the bytes do not carry field names. Prepare parses the schema once and copies the fixture into a value. The timed path is encodeReuse on a reused writer and decode. One instance is the struct. A batch is one list of that struct. Graph is not timed.

Not a @bitCast of the whole fixture

A live suite value has []const u8 slices. @bitCast of that type is not a portable encoding. comptime-bin is the honest idiomatic stand-in: comptime reflection writes a length-prefixed little-endian image.

Schema generation

These rows compile the same suite IDLs as the other languages. They do not invent Zig-only schemas.

Row Shared schema Regenerate
protobuf schemas/v2/protobuf/benchmark_v2.proto ./zig/scripts/generate-protobuf.sh
flatbuffers cpp/schemas/benchmark.fbs ./zig/scripts/generate-flatbuffers.sh
capnproto cpp/schemas/benchmark.capnp ./zig/scripts/generate-capnp.sh

Prepare copies each suite fixture into the library’s native form (untimed). The timer measures encode and decode only. Fidelity copies back to the suite struct after decode.

capnproto uses the official C++ library (like Swift) because the Zig Cap’n Proto plugin requires Zig 0.17-dev. The host needs libcapnp / libkj under ~/.local (./scripts/install-host-requirements.sh zig).

Dashboard