Skip to content

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 in cpp/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:

  1. yyjson (registered in both harnesses)
  2. Why: Written in C, ships yyjson.h with C linkage; C++ can call it without a separate C++ port.
  3. How: C++ includes yyjson.h and uses yyjson_read / yyjson_mut_write (same recommended APIs as the C harness).
  4. Example:

    #include <yyjson.h>
    yyjson_doc* doc = yyjson_read(ptr, len, 0);
    char* out = yyjson_write(doc, 0, &out_len);
    vs C harness ser_yyjson.c with the same calls.

  5. msgpack-c (related but not the same registration)

  6. Why: One repository provides two APIs: C (msgpack.h) and C++ (msgpack.hpp).
  7. How: C suite uses pack/unpack C functions; C++ suite uses msgpack::packer / msgpack::unpack.
  8. Wire format: Compatible MessagePack; call path and type mapping differ.

  9. Protobuf family (shared schema, different runtimes)

  10. Why: The suite .proto is language-agnostic; C and C++ use different encoders for the same field numbers.
  11. How: C β†’ nanopb / protobuf-c; C++ β†’ proto3 wire codec aligned with schemas/v2/protobuf/benchmark_v2.proto.
  12. Example field: Message.f_int32 = 2 is wire tag (2<<3)|0 in both.

  13. FlatBuffers family (shared idea, different codegens)

  14. Why: Google FlatBuffers is C++-first; flatcc is the maintained C implementation.
  15. How: C harness β†’ flatcc builder/reader; C++ harness β†’ flatbuffers::FlatBufferBuilder (+ FlexBuffers).
  16. Not interchangeable binaries without matching schema/codegen.

  17. Avro family

  18. Why: Same Avro binary encoding (zigzag ints, length-prefixed strings, array blocks).
  19. 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).
  20. Example: string = zigzag/long length + bytes; arrays end with a zero count block.

  21. Not dual-registered (C-only or C++-only by design)

  22. C-only in suite: cJSON, jansson, parson, json-c, mpack, tinycbor, QCBOR, libbson, nanopb, flatcc, avro-c, zcbor.
  23. 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 libprotobuf link β€” field layout matches schemas/v2/protobuf/benchmark_v2.proto.
  • flatbuffers blob-root path embeds suite payload via FlatBufferBuilder (typed tables generated when flatc runs).
  • Stream mode is native where the library exposes streams/buffers and the harness uses them (VecOutStream/VecInStream, Cap’n Proto writeMessage, 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

  1. Prepare outside the loop — DOM trees, packers, flexbuffers builders, domain→wire convert.
  2. Optimal APIs β€” library-recommended encode/decode; no pretty-print JSON.
  3. Dual mode β€” bytes and stream with StreamMode metadata.
  4. C++20 β€” ArduinoJson v7 / zpp_bits / modern std::variant fixtures.