C++
C++ serialization spans header-only JSON (nlohmann, RapidJSON, ArduinoJson), SIMD parse (simdjson), C libraries callable from C++ (yyjson), schemaless binary (MessagePack, cereal, bitsery, zpp_bits, CBOR/BSON via jsoncons), and schema / zero-copy families (Protobuf wire, FlatBuffers, FlexBuffers).
Harness
- Directory:
cpp/(repository root) - Output: monorepo
logs/cpp/YYYY-MM-DD-HHMMSS.csv(Language=cpp, times in nanoseconds) - Runner:
cpp/scripts/run-benchmarks.sh {smoke|all-single|full|research} - Build: CMake C++20, deps via
FetchContentβcpp/third_party/(pins incpp/third_party/VERSIONS.md) - Registration:
cpp/src/register.cpp
Serializers (26+)
| Serializer | Category | Library | Optimal call path | Notes |
|---|---|---|---|---|
| nlohmann_json | JSON | nlohmann/json | dump / parse; stream << / parse(istream) |
De-facto C++ JSON; native stream |
| rapidjson | JSON | Tencent/rapidjson | Writer + Document::Parse; stream O/IStreamWrapper |
SAX/DOM hot path; native stream |
| simdjson | JSON | simdjson | dom::parser::parse |
Ser = prepared minified JSON; stream adapted |
| arduinojson | JSON | ArduinoJson | serializeJson / deserializeJson (bytes + stream) |
Embedded/IoT; native stream |
| yyjson | JSON | yyjson | yyjson_mut_write / yyjson_read |
Also in C suite; stream adapted |
| msgpack | Binary | msgpack-c (C++ API) | packer + sbuffer / unpack; stream packer + unpacker |
Official C++ API; native stream |
| nlohmann_msgpack | Binary | nlohmann/json | to_msgpack / from_msgpack (+ ostream/istream) |
Multi-format nlohmann; native stream |
| nlohmann_cbor | Binary | nlohmann/json | to_cbor / from_cbor (+ ostream/istream) |
IETF CBOR; native stream |
| nlohmann_ubjson | Binary | nlohmann/json | to_ubjson / from_ubjson (+ ostream/istream) |
UBJSON; native stream |
| nlohmann_bson | Binary | nlohmann/json | to_bson / from_bson (+ ostream/istream) |
BSON (object root); native stream |
| cereal | Binary | cereal | BinaryOutput/InputArchive on ostream/istream |
C++-native archives; native stream |
| bitsery | Binary | bitsery | serializer object/container |
Explicit schema |
| zpp_bits | Binary | zpp_bits | zpp::bits::out / in |
Compile-time binary |
| yas | Binary | niXman/yas | yas::save/load mem\|binary |
Top-tier microbench staple |
| cista | Binary | Cista++ | cista::serialize / deserialize |
Offset graphs; convert in prepare |
| jsoncons_cbor | Binary | jsoncons | cbor::encode/decode (bytes + ostream/istream) |
DOM multi-format; native stream |
| jsoncons_bson | Binary | jsoncons | bson::encode/decode (bytes + ostream/istream) |
Document binary; native stream |
| jsoncons_msgpack | Binary | jsoncons | msgpack::encode/decode (bytes + ostream/istream) |
DOM MessagePack; native stream |
| custom_binary | Binary | harness | length-prefixed fields | Baseline; stream adapted |
| thrift | Schema | suite TBinaryProtocol | field type+id + STOP | Apache Thrift binary; stream adapted |
| avro_c | Schema | avro-c | cached iface + value_write/read | Real Avro C lib from C++; stream adapted |
| capnproto | Schema | Cap'n Proto | flat array bytes; writeMessage / InputStreamMessageReader stream |
Zero-copy schema; native stream |
| boost_serialization | Binary | Boost.Serialization | binary_o/iarchive (bytes + stream) | Optional (system lib); native stream |
| protobuf | Schema | suite wire | proto3 field tags | Shared .proto field numbers |
| avro | Schema | suite avro-binary | zigzag/varint + array blocks | Avro binary encoding |
| flexbuffers | Schema | flatbuffers | flexbuffers::Builder / GetRoot |
Schemaless FB family |
| flatbuffers | Schema | flatbuffers | FlatBufferBuilder |
C++ primary; C uses flatcc |
Call-path contract
prepare(fixture) # untimed: DOM/maps, buffers, domain convert
for rep:
serialize_bytes / stream # timed
deserialize_bytes / stream # timed (codec only)
to_domain (if needed) # untimed
fidelity(expected, actual) # untimed
C vs C++ β clear separation
| Concern | C harness (c/) |
C++ harness (cpp/) |
|---|---|---|
| Language id | c |
cpp |
| Object model | C structs + function pointers | C++20 structs + virtual ISerializer |
| JSON focus | cJSON, yyjson, jansson, parson, json-c | nlohmann, RapidJSON, simdjson, arduinojson, yyjson |
| MessagePack | mpack, msgpack-c C API | msgpack-c C++ API (msgpack.hpp) |
| CBOR | tinycbor, libcbor, QCBOR, zcbor | jsoncons CBOR |
| Protobuf | nanopb, protobuf-c, in-tree wire | suite proto3 wire (same field tags as .proto) |
| FlatBuffers | flatcc (C) | google/flatbuffers (C++) |
Libraries that work for both C and C++
Some projects are C libraries with a pure C API. They are valid from C++ via extern "C" includes. The suite registers them carefully:
- yyjson (registered in both harnesses)
- Why: Written in C, ships
yyjson.hwith C linkage; C++ can call it without a separate C++ port. - How: C++ includes
yyjson.hand usesyyjson_read/yyjson_mut_write(same recommended APIs as the C harness). -
Example:
vs C harness#include <yyjson.h> yyjson_doc* doc = yyjson_read(ptr, len, 0); char* out = yyjson_write(doc, 0, &out_len);ser_yyjson.cwith the same calls. -
msgpack-c (related but not the same registration)
- Why: One repository provides two APIs: C (
msgpack.h) and C++ (msgpack.hpp). - How: C suite uses pack/unpack C functions; C++ suite uses
msgpack::packer/msgpack::unpack. -
Wire format: Compatible MessagePack; call path and type mapping differ.
-
Protobuf family (shared schema, different runtimes)
- Why: The suite
.protois language-agnostic; C and C++ use different encoders for the same field numbers. - How: C β nanopb / protobuf-c; C++ β proto3 wire codec aligned with
schemas/v2/protobuf/benchmark_v2.proto. -
Example field:
Message.f_int32 = 2is wire tag(2<<3)|0in both. -
FlatBuffers family (shared idea, different codegens)
- Why: Google FlatBuffers is C++-first; flatcc is the maintained C implementation.
- How: C harness β flatcc builder/reader; C++ harness β
flatbuffers::FlatBufferBuilder(+ FlexBuffers). -
Not interchangeable binaries without matching schema/codegen.
-
Avro family
- Why: Same Avro binary encoding (zigzag ints, length-prefixed strings, array blocks).
- How: C harness β avro-c; C++ harness β in-tree Avro binary codec for suite types (Apache avro-cpp is heavy to FetchContent; wire follows Avro 1.x binary).
-
Example:
string= zigzag/longlength + bytes; arrays end with a zero count block. -
Not dual-registered (C-only or C++-only by design)
- C-only in suite: cJSON, jansson, parson, json-c, mpack, tinycbor, QCBOR, libbson, nanopb, flatcc, avro-c, zcbor.
- C++-only in suite: nlohmann, RapidJSON, simdjson, arduinojson, cereal, bitsery, zpp_bits, jsoncons, google flatbuffers C++ API.
Rule of thumb: If a library is pure C and already measured under Language=c, re-registering under C++ only makes sense when the C++ call path is a first-class usage mode (yyjson) or when the API surface differs (msgpack C vs C++). Do not treat C and C++ rows as interchangeable runtimes for ranking.
Suite fixtures
Type ids: message, document, telemetry, strings, event.
Caveats
- simdjson is optimized for parse; serialize is prepared minified JSON (same honesty as Rust/JS suite entries).
- protobuf uses an in-tree proto3 wire codec for the suite schema (standard tags), not a full
libprotobuflink β field layout matchesschemas/v2/protobuf/benchmark_v2.proto. - flatbuffers blob-root path embeds suite payload via
FlatBufferBuilder(typed tables generated whenflatcruns). - Stream mode is native where the library exposes streams/buffers and the harness uses them (
VecOutStream/VecInStream, Capβn ProtowriteMessage, msgpack packer/unpacker, etc.); others are adapted (stream path = bytes path). - First CMake configure downloads pinned deps into
cpp/third_party/(network required once).
Also: cpp/README.md. Serialization Categories.
Design choices
- Prepare outside the loop β DOM trees, packers, flexbuffers builders, domainβwire convert.
- Optimal APIs β library-recommended encode/decode; no pretty-print JSON.
- Dual mode β
bytesandstreamwithStreamModemetadata. - C++20 β ArduinoJson v7 / zpp_bits / modern
std::variantfixtures.