Skip to content

Swift

Swift’s serialization stack mixes Codable codecs (Foundation JSON/plist, IkigaJSON, MessagePack, CBOR, BSON, YAML, XML) with schema/IDL stacks (SwiftProtobuf, FlatBuffers, Avro, Cap’n Proto, Dagr).

Runtime

What it is

Swift compiles to native machine code. Memory is managed with ARC (Automatic Reference Counting). An object is freed when the last reference to it goes away. That is not the same as the tracing garbage collector used by .NET or the JVM. Swift is not treated here as an Apple-only language. This runner is built and timed on Linux as well.

This suite
Tools Swift 5.10 or newer (Package.swift). The install script places Swift 6.x under ~/.local/swift.
Build Swift Package Manager (swift build -c release)
Prepare ./scripts/install-host-requirements.sh swift
Run swift/scripts/run-benchmarks.sh
Memory Automatic reference counting, not a tracing garbage collector

What this suite runs

The runner is built in the release configuration, which turns on optimizations. Codable wrappers never import the suite types. They see a type-erased Fixture value instead. Schema codecs (Protobuf, FlatBuffers, Avro, Cap’n Proto) convert between the suite objects and each library’s native type outside the timer.

What changes the numbers

ARC still has a cost: every extra retain and release is work. Foundation JSON on Linux is not the same binary as Foundation JSON on Apple platforms. Cap’n Proto in this suite is the official C++ library, reached through CapnpBridge and libcapnp / libkj under ~/.local. It is not a pure-Swift runtime.

Suite-specific gotchas

Stream mode is adapted for every registered codec. The timed path is still bytes, then a write or read of those bytes.

Linux TOML builds may need GCC 11 libstdc++ include flags. The run script already sets those flags.

These times cannot be ranked against another language.

Where to go next

The steps to install the toolchain and run the benchmark are in swift/README.md. The language overview is About Swift.

Benchmark runner

Serializers

Serializer Category Package Stream Notes
BinaryCodable Binary BinaryCodable adapted Pure-Swift binary Codable
CapnProto Schema Cap’n Proto C++ adapted C ABI over official C++ runtime
dagr-packed Schema generated (swift/DagrGen, dagr build) adapted Packed nodes; generated direct builder → reused DataArenaBuilder; lazy reader → domain (timed). N>1 framed by the wrapper (u32 count + (u32 len + buffer)×N)
dagr-regular Schema generated (swift/DagrGen, dagr build) adapted Regular (vtable) nodes, <T>RegularGraph; suite value → generated arena → root stored into a reused DataArenaBuilder (timed); lazy reader → domain. N>1: one self-contained record per item, same frame
dagr-frozen Schema generated (swift/DagrGen, dagr build) adapted Frozen nodes, <T>FrozenGraph; same call path as dagr-regular
dagr-frozen-packed Schema generated (swift/DagrGen, dagr build) adapted Frozen+packed nodes, <T>FrozenPackedGraph; same call path as dagr-packed (direct builder)
FlatBuffers Schema google/flatbuffers adapted Generated from suite .fbs
fory Binary apache/fory adapted ForyStruct on suite value types; registration outside timing
Foundation.JSONEncoder JSON Foundation adapted Compact
Foundation.PropertyListEncoder Native Foundation adapted Binary plist
IkigaJSON JSON IkigaJSON adapted Server JSON
SwiftAvroCore Schema SwiftAvroCore adapted Binary Avro + schema
gld-avro Schema leo-gan/gld-avro-swift 0.2.0 adapted parseAvsc once; encodeGeneric / decodeGeneric
gld-ion Binary leo-gan/gld-ion-swift 0.2.0 adapted Ion 1.0 binary encode / decodeBinary
gld-kiln Binary leo-gan/gld-kiln-swift 0.2.0 adapted Positional Writer.encode / decode; a batch is one list
SwiftBSON Binary swift-bson adapted Map-root wrap for N>1
SwiftCbor Binary swift-cbor adapted Codable CBOR
SwiftMsgpack Binary swift-msgpack adapted Codable MessagePack
SwiftProtobuf Schema apple/swift-protobuf adapted Generated from suite .proto
TOML Text mattt/swift-toml adapted Map-root wrap for N>1
XMLCoder Text XMLCoder adapted Root payload
Yams Text Yams adapted YAML

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.

BinaryCodable · 4.0.0

BinaryCodable is a pure-Swift binary Codable implementation. It was written so Swift types could have a simple binary encoding without an IDL — Swift-only, not a public standard.

CapnProto · 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.

dagr-packed · 2026.10.1

Dagr ("Data Graph") is a schema-driven binary format that can store shared nodes and cycles, built on an arena model. One Python DSL schema generates the code for every target language (dagr build), so there is no runtime library: the suite commits the generated code from schemas/v2/dagr/schema.py. The five suite types are emitted in all four node layouts, one row each. The graph data type is emitted only for regular and frozen, because a packed reference cannot store the person ring. On suite types, prepare builds the native value and the timed call writes every field (direct builder or arena serializer) and reads them back. This row uses the packed node layout (tagged, evolvable). It does not support the graph data type.

dagr-regular · 2026.10.1

Dagr ("Data Graph") is a schema-driven binary format that can store shared nodes and cycles, built on an arena model. One Python DSL schema generates the code for every target language (dagr build), so there is no runtime library: the suite commits the generated code from schemas/v2/dagr/schema.py. The five suite types are emitted in all four node layouts, one row each. The graph data type is emitted only for regular and frozen, because a packed reference cannot store the person ring. On suite types, prepare builds the native value and the timed call writes every field (direct builder or arena serializer) and reads them back. This row uses the regular node layout (vtable, evolvable). It also times the graph data type.

dagr-frozen · 2026.10.1

Dagr ("Data Graph") is a schema-driven binary format that can store shared nodes and cycles, built on an arena model. One Python DSL schema generates the code for every target language (dagr build), so there is no runtime library: the suite commits the generated code from schemas/v2/dagr/schema.py. The five suite types are emitted in all four node layouts, one row each. The graph data type is emitted only for regular and frozen, because a packed reference cannot store the person ring. On suite types, prepare builds the native value and the timed call writes every field (direct builder or arena serializer) and reads them back. This row uses the frozen node layout (positional, no evolution). It also times the graph data type.

dagr-frozen-packed · 2026.10.1

Dagr ("Data Graph") is a schema-driven binary format that can store shared nodes and cycles, built on an arena model. One Python DSL schema generates the code for every target language (dagr build), so there is no runtime library: the suite commits the generated code from schemas/v2/dagr/schema.py. The five suite types are emitted in all four node layouts, one row each. The graph data type is emitted only for regular and frozen, because a packed reference cannot store the person ring. On suite types, prepare builds the native value and the timed call writes every field (direct builder or arena serializer) and reads them back. This row uses the frozen+packed node layout (positional and inline, no evolution). It does not support the graph data type.

FlatBuffers · 24.3.25

FlatBuffers was created at Google so games and clients could access serialized data without an unpack step. The problem was that protobuf-style decode allocated a full object graph. FlatBuffers solves it with a schema and a binary layout that can be traversed in place.

fory

Apache Fory (formerly Fury) was created for high-performance, cross-language serialization. The problem was that JVM-centric binary codecs and slow portable formats left a gap. Fory registers types and serializes with a compact binary protocol. This row uses ForyStruct macro-generated serializers on Swift value types.

Foundation.JSONEncoder · Foundation

Foundation's JSONEncoder/JSONDecoder are Apple's standard Codable JSON codecs. They exist so Swift can speak JSON with the language's Codable model. On Linux this is swift-corelibs-foundation, not the Apple OS binary.

Foundation.PropertyListEncoder · Foundation

Foundation PropertyListEncoder writes Apple property lists. plists exist so Apple platforms can store typed configuration. This row times the binary plist path.

IkigaJSON · 2.5.4

IkigaJSON is a server-oriented JSON encoder/decoder for Swift. The problem was Foundation JSON performance on Linux servers. IkigaJSON implements Codable with a faster core.

SwiftAvroCore · 2.3.0

SwiftAvroCore implements Apache Avro for Swift. Avro exists for compact, schema-driven records. The library encodes binary Avro with a schema.

gld-avro · 0.2.0

gld-avro is a registered serializer in the swift suite. This page links its upstream source; the language inventory table describes the timed call path.

gld-ion · 0.2.0

gld-ion is a registered serializer in the swift suite. This page links its upstream source; the language inventory table describes the timed call path.

gld-kiln · 0.2.0

gld-kiln is a positional little-endian codec. The schema is the struct field order, built once in prepare, so the timed path does not write names or tags. Writer.encode reuses one buffer; decode reads the bytes back into the suite structs. A batch of 100 is one list of that struct. The wire is Kiln wire v2.

SwiftBSON · 3.1.0

swift-bson is MongoDB's official BSON library for Swift. BSON exists so MongoDB can store typed documents. This package implements that spec for Codable-style use.

SwiftCbor · 0.0.4

swift-cbor is a Codable CBOR implementation for Swift. CBOR is the IETF binary JSON-like format. The library maps Codable types to RFC 8949.

SwiftMsgpack · 1.2.1

swift-msgpack is a Codable MessagePack implementation for Swift. MessagePack exists as compact binary JSON. This library is a straightforward Codable backend.

SwiftProtobuf · 1.38.1

swift-protobuf is Apple's official Protocol Buffers runtime for Swift. Protobuf exists as a language-neutral IDL and wire format. This package generates Swift from .proto.

TOML · 2.0.0

mattt/swift-toml (toml++) is a TOML library for Swift. TOML exists as an obvious config language. This row times that implementation (map-root wrap for N>1).

XMLCoder · 0.18.2

XMLCoder is a Codable XML encoder/decoder for Swift. The problem was that Swift had JSON Codable but not a first-class XML Codable. XMLCoder maps Codable types to XML elements.

Yams · 5.4.0

Yams is a Swift wrapper around libyaml. YAML exists as a human-friendly config language. Yams is the usual Swift YAML library.

Call-path contract

prepare(fixture)                 # untimed: schema, native message / builder state
serialize_bytes / stream         # timed
deserialize_bytes / stream       # timed (+ domain conversion for schema codecs)
fidelity                         # untimed, float-tolerant

Codable wrappers never import suite types. Schema bridges convert domain ↔ native in prepare / after deserialize (same pattern as Go protobuf / Rust prost).

Caveats

  • Stream mode is adapted for all registered codecs.
  • Cap’n Proto has no maintained first-class Swift codegen; the benchmark runner uses the official C++ library via CapnpBridge (requires libcapnp / libkj, typically under ~/.local).
  • TOML uses mattt/swift-toml (toml++); Linux builds may need GCC 11 libstdc++ include flags (set in run-benchmarks.sh).
  • dagr-packed call path: timed serialize builds the generated <Graph>.Direct.* value structs from the suite value (like SwiftProtobuf's toProtobuf) and stores them into one reused DataArenaBuilder; timed deserialize walks the generated lazy accessors and builds the suite value. SerializerVersion is the generator version from schemas/v2/dagr/dagr.lock.json. Batch items are read in place with the generated lazyRoot(from:at:) (no per-item copy). The remaining gap on telemetry decode is one Data.withUnsafeBytes per raw f64 element.
  • dagr-regular / dagr-frozen / dagr-frozen-packed: one DagrSerializer(layout:) class shares the harness with dagr-packed (Serializers/Dagr.swift, bridges in Serializers/DagrLayouts.swift). frozen-packed has a generated direct builder and is timed exactly like dagr-packed. regular and frozen have none, so timed serialize builds a fresh generated <Graph>.Arena from the suite value and stores its root into the reused DataArenaBuilder (what Arena.toData() does, minus its per-call builder); their lazy getters are get throws. Regular/frozen stores go through the builder's dedup tables (strings, vtables, node ids) that only reset() clears, so for N>1 each item is encoded as its own self-contained record and copied into the frame (one extra copy per item) — no cross-item dedup.

Also: swift/README.md.

Numbers

Measured numbers for this language live on the Dashboard (pre-filtered). Claim level is L1 (one machine, one session) — see Claims and replication.