C++ Serialization Library provides functionality to serialize/deserialize objects using different protocols such as Cap'n'Proto, FastBinaryEncoding, Flatbuffers, Protobuf, SimpleBinaryEncoding, zpp::bits, JSON.
Performance comparison based on the Domain model with one account, one wallet and three orders total size of 128 bytes:
CppSerialization API reference
Optional:
Linux: install required packagessudo apt-get install -y binutils-dev uuid-dev
git clone https://github.com/chronoxor/CppSerialization.git cd CppSerialization gil update
The first step you should perform to use CppSerialization library is to provide a domain model (aka business objects). Domain model is a set of structures or classes that related to each other and might be aggregated in some hierarchy.
There is an example domain model which describes Account-Balance-Orders relation of some abstract trading platform:
#include <string> #include <vector> namespace TradeProto { enum class OrderSide : uint8_t { BUY, SELL }; enum class OrderType : uint8_t { MARKET, LIMIT, STOP }; struct Order { int Id; char Symbol[10]; OrderSide Side; OrderType Type; double Price; double Volume; Order() : Order(0, "<\?\?\?>", OrderSide::BUY, OrderType::MARKET, 0.0, 0.0) {} Order(int id, const std::string& symbol, OrderSide side, OrderType type, double price, double volume) { Id = id; std::memcpy(Symbol, symbol.c_str(), std::min(symbol.size() + 1, sizeof(Symbol))); Side = side; Type = type; Price = price; Volume = volume; } }; struct Balance { char Currency[10]; double Amount; Balance() : Balance("<\?\?\?>", 0.0) {} Balance(const std::string& currency, double amount) { std::memcpy(Currency, currency.c_str(), std::min(currency.size() + 1, sizeof(Currency))); Amount = amount; } }; struct Account { int Id; std::string Name; Balance Wallet; std::vector<Order> Orders; Account() : Account(0, "<\?\?\?>", "<\?\?\?>", 0.0) {} Account(int id, const char* name, const char* currency, double amount) : Wallet(currency, amount) { Id = id; Name = name; } }; } // namespace TradeProto
The next step you should provide serialization methods for the domain model.
Cap'n'Proto serializationCap'n'Proto serialization is based on Cap'n'Proto library.
Cap'n'Proto serialization starts with describing a model schema. For our domain model the schema will be the following:
# Unique file ID, generated by 'capnp id' @0xd4b6e00623bed170; using Cxx = import "/capnp/c++.capnp"; $Cxx.namespace("Trade::capnproto"); enum OrderSide { buy @0; sell @1; } enum OrderType { market @0; limit @1; stop @2; } struct Order { id @0 : Int32; symbol @1 : Text; side @2 : OrderSide; type @3 : OrderType; price @4 : Float64 = 0.0; volume @5 : Float64 = 0.0; } struct Balance { currency @0 : Text; amount @1 : Float64 = 0.0; } struct Account { id @0 : Int32; name @1 : Text; wallet @2 : Balance; orders @3 : List(Order); }Cap'n'Proto schema compilation
The next step is a schema compilation using 'capnpc' utility which will create a generated code for required programming language.
The following command will create a C++ generated code:
capnp compile -I capnproto/c++/src -oc++ trade.capnp
It is possible to use capnp_generate_cpp() in CMakeLists.txt to generate code using 'cmake' utility:
capnp_generate_cpp(CAPNP_HEADERS CAPNP_SOURCES trade.capnp)
As the result 'trade.capnp.h' and 'trade.capnp.c++' files will be generated.
Cap'n'Proto serialization methodsFinally you should extend your domain model with a Cap'n'Proto serialization methods:
#include "capnp/serialize.h" #include "capnproto/trade.capnp.h" #include <algorithm> namespace TradeProto { struct Order { ... // Cap'n'Proto serialization void Serialize(Trade::capnproto::Order::Builder& builder) { builder.setId(Id); builder.setSymbol(Symbol); builder.setSide((Trade::capnproto::OrderSide)Side); builder.setType((Trade::capnproto::OrderType)Type); builder.setPrice(Price); builder.setVolume(Volume); } void Deserialize(const Trade::capnproto::Order::Reader& reader) { Id = reader.getId(); std::string symbol = reader.getSymbol(); std::memcpy(Symbol, symbol.c_str(), std::min(symbol.size() + 1, sizeof(Symbol))); Side = (OrderSide)reader.getSide(); Type = (OrderType)reader.getType(); Price = reader.getPrice(); Volume = reader.getVolume(); } ... }; struct Balance { ... // Cap'n'Proto serialization void Serialize(Trade::capnproto::Balance::Builder& builder) { builder.setCurrency(Currency); builder.setAmount(Amount); } void Deserialize(const Trade::capnproto::Balance::Reader& reader) { std::string currency = reader.getCurrency(); std::memcpy(Currency, currency.c_str(), std::min(currency.size() + 1, sizeof(Currency))); Amount = reader.getAmount(); } ... }; struct Account { ... // Cap'n'Proto serialization void Serialize(Trade::capnproto::Account::Builder& builder) { builder.setId(Id); builder.setName(Name); auto wallet = builder.initWallet(); Wallet.Serialize(wallet); auto orders = builder.initOrders((unsigned)Orders.size()); unsigned index = 0; for (auto& order : Orders) { auto o = orders[index++]; order.Serialize(o); } } void Deserialize(const Trade::capnproto::Account::Reader& reader) { Id = reader.getId(); Name = reader.getName().cStr(); Wallet.Deserialize(reader.getWallet()); Orders.clear(); for (auto o : reader.getOrders()) { Order order; order.Deserialize(o); Orders.emplace_back(order); } } ... }; } // namespace TradeProto
Here comes the usage example of Cap'n'Proto serialize/deserialize functionality:
#include "../proto/trade.h" #include <iostream> int main(int argc, char** argv) { // Create a new account with some orders TradeProto::Account account(1, "Test", "USD", 1000); account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000)); account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100)); account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10)); // Serialize the account to the Cap'n'Proto buffer capnp::MallocMessageBuilder output; Trade::capnproto::Account::Builder builder = output.initRoot<Trade::capnproto::Account>(); account.Serialize(builder); kj::VectorOutputStream buffer; writeMessage(buffer, output); // Show original and Cap'n'Proto serialized sizes std::cout << "Original size: " << account.size() << std::endl; std::cout << "Cap'n'Proto size: " << buffer.getArray().size() << std::endl; // Deserialize the account from the Cap'n'Proto buffer kj::ArrayInputStream array(buffer.getArray()); capnp::InputStreamMessageReader input(array); TradeProto::Account deserialized; deserialized.Deserialize(input.getRoot<Trade::capnproto::Account>()); // Show account content std::cout << std::endl; std::cout << "Account.Id = " << deserialized.Id << std::endl; std::cout << "Account.Name = " << deserialized.Name << std::endl; std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl; std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl; for (auto& order : deserialized.Orders) { std::cout << "Account.Order => Id: " << order.Id << ", Symbol: " << order.Symbol << ", Side: " << (int)order.Side << ", Type: " << (int)order.Type << ", Price: " << order.Price << ", Volume: " << order.Volume << std::endl; } return 0; }
Output of the example is the following:
Original size: 128
Cap'n'Proto size: 208
Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10
Cap'n'Proto serialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.136 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:05:35 2025
UTC timestamp: Wed Jul 16 17:05:35 2025
===============================================================================
Benchmark: Cap'n'Proto-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: Cap'n'Proto-Serialize
Average time: 247 ns/op
Minimal time: 244 ns/op
Maximal time: 254 ns/op
Total time: 4.762 s
Total operations: 19242126
Total bytes: 4.484 GiB
Operations throughput: 4040127 ops/s
Bytes throughput: 961.717 MiB/s
Custom values:
MessageSize: 208
OriginalSize: 128
===============================================================================
Cap'n'Proto deserialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.116 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:06:37 2025
UTC timestamp: Wed Jul 16 17:06:37 2025
===============================================================================
Benchmark: Cap'n'Proto-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: Cap'n'Proto-Deserialize
Average time: 184 ns/op
Minimal time: 183 ns/op
Maximal time: 184 ns/op
Total time: 4.959 s
Total operations: 26913647
Total bytes: 6.262 GiB
Operations throughput: 5426756 ops/s
Bytes throughput: 1.267 GiB/s
Custom values:
MessageSize: 208
OriginalSize: 128
===============================================================================
FastBinaryEncoding serialization
FastBinaryEncoding serialization is based on FastBinaryEncoding library.
FastBinaryEncoding schemaFastBinaryEncoding serialization starts with describing a model schema. For our domain model the schema will be the following:
package trade enum OrderSide : byte { buy; sell; } enum OrderType : byte { market; limit; stop; } struct Order { [key] int32 id; string symbol; OrderSide side; OrderType type; double price = 0.0; double volume = 0.0; } struct Balance { [key] string currency; double amount = 0.0; } struct Account { [key] int32 id; string name; Balance wallet; Order[] orders; }FastBinaryEncoding schema compilation
The next step is a schema compilation using 'fbec' utility which will create a generated code for required programming language.
The following command will create a C++ generated code:
fbec --cpp --input=trade.fbe --output=.
It is possible to use add_custom_command() in CMakeLists.txt to generate code using 'cmake' utility:
add_custom_command(TARGET example POST_BUILD COMMAND fbec --cpp --input=trade.fbe --output=.)
As the result 'fbe.h' and 'trade.h' files will be generated.
FastBinaryEncoding serialization methodsFinally you should extend your domain model with a FastBinaryEncoding serialization methods:
#include "fbe/trade_models.h" #include <algorithm> namespace TradeProto { struct Order { ... // FastBinaryEncoding serialization template <class TBuffer> void Serialize(FBE::FieldModel<TBuffer, trade::Order>& model) { size_t model_begin = model.set_begin(); model.id.set(Id); model.symbol.set(Symbol); model.side.set((trade::OrderSide)Side); model.type.set((trade::OrderType)Type); model.price.set(Price); model.volume.set(Volume); model.set_end(model_begin); } template <class TBuffer> void Deserialize(const FBE::FieldModel<TBuffer, trade::Order>& model) { size_t model_begin = model.get_begin(); model.id.get(Id); model.symbol.get(Symbol); trade::OrderSide side; model.side.get(side); Side = (OrderSide)side; trade::OrderType type; model.type.get(type); Type = (OrderType)type; model.price.get(Price); model.volume.get(Volume); model.get_end(model_begin); } ... }; struct Balance { ... // FastBinaryEncoding serialization template <class TBuffer> void Serialize(FBE::FieldModel<TBuffer, trade::Balance>& model) { size_t model_begin = model.set_begin(); model.currency.set(Currency); model.amount.set(Amount); model.set_end(model_begin); } template <class TBuffer> void Deserialize(const FBE::FieldModel<TBuffer, trade::Balance>& model) { size_t model_begin = model.get_begin(); model.currency.get(Currency); model.amount.get(Amount); model.get_end(model_begin); } ... }; struct Account { ... // FastBinaryEncoding serialization template <class TBuffer> void Serialize(FBE::FieldModel<TBuffer, trade::Account>& model) { size_t model_begin = model.set_begin(); model.id.set(Id); model.name.set(Name); Wallet.Serialize(model.wallet); auto order_model = model.orders.resize(Orders.size()); for (auto& order : Orders) { order.Serialize(order_model); order_model.fbe_shift(order_model.fbe_size()); } model.set_end(model_begin); } template <class TBuffer> void Deserialize(const FBE::FieldModel<TBuffer, trade::Account>& model) { size_t model_begin = model.get_begin(); model.id.get(Id); model.name.get(Name); Wallet.Deserialize(model.wallet); Orders.clear(); for (size_t i = 0; i < model.orders.size(); ++i) { Order order; order.Deserialize(model.orders[i]); Orders.emplace_back(order); } model.get_end(model_begin); } ... }; } // namespace TradeProtoFastBinaryEncoding example
Here comes the usage example of FastBinaryEncoding serialize/deserialize functionality:
#include "../proto/trade.h" #include <iostream> int main(int argc, char** argv) { // Create a new account with some orders TradeProto::Account account(1, "Test", "USD", 1000); account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000)); account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100)); account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10)); // Serialize the account to the FBE buffer FBE::trade::AccountModel<FBE::WriteBuffer> writer; size_t model_begin = writer.create_begin(); account.Serialize(writer.model); size_t serialized = writer.create_end(model_begin); assert(writer.verify() && "Model is broken!"); // Show original and FBE serialized sizes std::cout << "Original size: " << account.size() << std::endl; std::cout << "FBE size: " << serialized << std::endl; // Deserialize the account from the FBE buffer TradeProto::Account deserialized; FBE::trade::AccountModel<FBE::ReadBuffer> reader; reader.attach(writer.buffer()); assert(reader.verify() && "Model is broken!"); deserialized.Deserialize(reader.model); // Show account content std::cout << std::endl; std::cout << "Account.Id = " << deserialized.Id << std::endl; std::cout << "Account.Name = " << deserialized.Name << std::endl; std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl; std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl; for (auto& order : deserialized.Orders) { std::cout << "Account.Order => Id: " << order.Id << ", Symbol: " << order.Symbol << ", Side: " << (int)order.Side << ", Type: " << (int)order.Type << ", Price: " << order.Price << ", Volume: " << order.Volume << std::endl; } return 0; }
Output of the example is the following:
Original size: 128
FBE size: 234
Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10
FastBinaryEncoding performance
FastBinaryEncoding serialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.179 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:08:14 2025
UTC timestamp: Wed Jul 16 17:08:14 2025
===============================================================================
Benchmark: FastBinaryEncoding-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: FastBinaryEncoding-Serialize
Average time: 77 ns/op
Minimal time: 77 ns/op
Maximal time: 81 ns/op
Total time: 5.204 s
Total operations: 66877702
Total bytes: 17.501 GiB
Operations throughput: 12849579 ops/s
Bytes throughput: 3.369 GiB/s
Custom values:
MessageSize: 234
OriginalSize: 128
===============================================================================
FastBinaryEncoding deserialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.194 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:10:03 2025
UTC timestamp: Wed Jul 16 17:10:03 2025
===============================================================================
Benchmark: FastBinaryEncoding-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: FastBinaryEncoding-Deserialize
Average time: 84 ns/op
Minimal time: 82 ns/op
Maximal time: 85 ns/op
Total time: 5.271 s
Total operations: 62074518
Total bytes: 16.239 GiB
Operations throughput: 11775531 ops/s
Bytes throughput: 3.081 GiB/s
Custom values:
MessageSize: 234
OriginalSize: 128
===============================================================================
FlatBuffers serialization
FlatBuffers serialization is based on FlatBuffers library.
FlatBuffers serialization starts with describing a model schema. For our domain model the schema will be the following:
namespace Trade.flatbuf; enum OrderSide : byte { buy, sell } enum OrderType : byte { market, limit, stop } table Order { id : int; symbol : string; side : OrderSide; type : OrderType; price : double = 0.0; volume : double = 0.0; } table Balance { currency : string; amount : double = 0.0; } table Account { id : int; name : string; wallet : Balance; orders : [Order]; } root_type Account;FlatBuffers schema compilation
The next step is a schema compilation using 'flatc' utility which will create a generated code for required programming language.
The following command will create a C++ generated code:
flatc --cpp --scoped-enums -o . trade.fbs
It is possible to use add_custom_command() in CMakeLists.txt to generate code using 'cmake' utility:
add_custom_command(TARGET example POST_BUILD COMMAND flatc --cpp --scoped-enums -o . trade.fbs)
As the result 'domain_generated.h' file will be generated.
FlatBuffers serialization methodsFinally you should extend your domain model with a FlatBuffers serialization methods:
#include "flatbuffers/trade_generated.h" #include <algorithm> namespace TradeProto { struct Order { ... // FlatBuffers serialization flatbuffers::Offset<Trade::flatbuf::Order> Serialize(flatbuffers::FlatBufferBuilder& builder) { return Trade::flatbuf::CreateOrderDirect(builder, Id, Symbol, (Trade::flatbuf::OrderSide)Side, (Trade::flatbuf::OrderType)Type, Price, Volume); } void Deserialize(const Trade::flatbuf::Order& value) { Id = value.id(); std::string symbol = value.symbol()->str(); std::memcpy(Symbol, symbol.c_str(), std::min(symbol.size() + 1, sizeof(Symbol))); Side = (OrderSide)value.side(); Type = (OrderType)value.type(); Price = value.price(); Volume = value.volume(); } ... }; struct Balance { ... // FlatBuffers serialization flatbuffers::Offset<Trade::flatbuf::Balance> Serialize(flatbuffers::FlatBufferBuilder& builder) { return Trade::flatbuf::CreateBalanceDirect(builder, Currency, Amount); } void Deserialize(const Trade::flatbuf::Balance& value) { std::string currency = value.currency()->str(); std::memcpy(Currency, currency.c_str(), std::min(currency.size() + 1, sizeof(Currency))); Amount = value.amount(); } ... }; struct Account { ... // FlatBuffers serialization flatbuffers::Offset<Trade::flatbuf::Account> Serialize(flatbuffers::FlatBufferBuilder& builder) { auto wallet = Wallet.Serialize(builder); std::vector<flatbuffers::Offset<Trade::flatbuf::Order>> orders; for (auto& order : Orders) orders.emplace_back(order.Serialize(builder)); return Trade::flatbuf::CreateAccountDirect(builder, Id, Name.c_str(), wallet, &orders); } void Deserialize(const Trade::flatbuf::Account& value) { Id = value.id(); Name = value.name()->str(); Wallet.Deserialize(*value.wallet()); Orders.clear(); for (auto o : *value.orders()) { Order order; order.Deserialize(*o); Orders.emplace_back(order); } } ... }; } // namespace TradeProto
Here comes the usage example of FlatBuffers serialize/deserialize functionality:
#include "../proto/trade.h" #include <iostream> int main(int argc, char** argv) { // Create a new account with some orders TradeProto::Account account(1, "Test", "USD", 1000); account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000)); account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100)); account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10)); // Serialize the account to the FlatBuffer buffer flatbuffers::FlatBufferBuilder builder; builder.Finish(account.Serialize(builder)); // Show original and FlatBuffer serialized sizes std::cout << "Original size: " << account.size() << std::endl; std::cout << "FlatBuffer size: " << builder.GetSize() << std::endl; // Deserialize the account from the FlatBuffer buffer TradeProto::Account deserialized; deserialized.Deserialize(*Trade::flatbuf::GetAccount(builder.GetBufferPointer())); // Show account content std::cout << std::endl; std::cout << "Account.Id = " << deserialized.Id << std::endl; std::cout << "Account.Name = " << deserialized.Name << std::endl; std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl; std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl; for (auto& order : deserialized.Orders) { std::cout << "Account.Order => Id: " << order.Id << ", Symbol: " << order.Symbol << ", Side: " << (int)order.Side << ", Type: " << (int)order.Type << ", Price: " << order.Price << ", Volume: " << order.Volume << std::endl; } return 0; }
Output of the example is the following:
Original size: 128
FlatBuffer size: 280
Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10
FlatBuffers serialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.270 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:11:16 2025
UTC timestamp: Wed Jul 16 17:11:16 2025
===============================================================================
Benchmark: FlatBuffers-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: FlatBuffers-Serialize
Average time: 272 ns/op
Minimal time: 272 ns/op
Maximal time: 273 ns/op
Total time: 4.947 s
Total operations: 18131562
Total bytes: 5.689 GiB
Operations throughput: 3664742 ops/s
Bytes throughput: 1.150 GiB/s
Custom values:
MessageSize: 280
OriginalSize: 128
===============================================================================
FlatBuffers deserialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.323 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:13:23 2025
UTC timestamp: Wed Jul 16 17:13:23 2025
===============================================================================
Benchmark: FlatBuffers-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: FlatBuffers-Deserialize
Average time: 81 ns/op
Minimal time: 77 ns/op
Maximal time: 81 ns/op
Total time: 5.320 s
Total operations: 65603873
Total bytes: 20.541 GiB
Operations throughput: 12329743 ops/s
Bytes throughput: 3.878 GiB/s
Custom values:
MessageSize: 280
OriginalSize: 128
===============================================================================
Protobuf serialization is based on Protobuf library.
Protobuf serialization starts with describing a model schema. For our domain model the schema will be the following:
syntax = "proto3"; package Trade.protobuf; enum OrderSide { buy = 0; sell = 1; } enum OrderType { market = 0; limit = 1; stop = 2; } message Order { int32 id = 1; string symbol = 2; OrderSide side = 3; OrderType type = 4; double price = 5; double volume = 6; } message Balance { string currency = 1; double amount = 2; } message Account { int32 id = 1; string name = 2; Balance wallet = 3; repeated Order orders = 4; }Protobuf schema compilation
The next step is a schema compilation using 'protoc' utility which will create a generated code for required programming language.
The following command will create a C++ generated code:
protoc --proto_path=. --cpp_out=. trade.proto
It is possible to use add_custom_command() in CMakeLists.txt to generate code using 'cmake' utility:
add_custom_command(TARGET example POST_BUILD COMMAND protoc --proto_path=. --cpp_out=. trade.proto)
As the result 'trade.pb.h' and 'trade.pb.cc' files will be generated.
Protobuf serialization methodsFinally you should extend your domain model with a FlatBuffers serialization methods:
#include "protobuf/trade.pb.h" #include <algorithm> namespace TradeProto { struct Order { ... // Protobuf serialization Trade::protobuf::Order& Serialize(Trade::protobuf::Order& value) { value.set_id(Id); value.set_symbol(Symbol); value.set_side((Trade::protobuf::OrderSide)Side); value.set_type((Trade::protobuf::OrderType)Type); value.set_price(Price); value.set_volume(Volume); return value; } void Deserialize(const Trade::protobuf::Order& value) { Id = value.id(); std::string symbol = value.symbol(); std::memcpy(Symbol, symbol.c_str(), std::min(symbol.size() + 1, sizeof(Symbol))); Side = (OrderSide)value.side(); Type = (OrderType)value.type(); Price = value.price(); Volume = value.volume(); } ... }; struct Balance { ... // Protobuf serialization Trade::protobuf::Balance& Serialize(Trade::protobuf::Balance& value) { value.set_currency(Currency); value.set_amount(Amount); return value; } void Deserialize(const Trade::protobuf::Balance& value) { std::string currency = value.currency(); std::memcpy(Currency, currency.c_str(), std::min(currency.size() + 1, sizeof(Currency))); Amount = value.amount(); } ... }; struct Account { ... // Protobuf serialization Trade::protobuf::Account& Serialize(Trade::protobuf::Account& value) { value.set_id(id); value.set_name(Name); value.set_allocated_wallet(&Wallet.Serialize(*value.wallet().New(value.GetArena()))); for (auto& order : Orders) order.Serialize(*value.add_orders()); return value; } void Deserialize(const Trade::protobuf::Account& value) { Id = value.id(); Name = value.name(); Wallet.Deserialize(value.wallet()); Orders.clear(); for (int i = 0; i < value.orders_size(); ++i) { Order order; order.Deserialize(value.orders(i)); Orders.emplace_back(order); } } ... }; } // namespace TradeProto
Here comes the usage example of Protobuf serialize/deserialize functionality:
#include "../proto/trade.h" #include <iostream> int main(int argc, char** argv) { // Create a new account with some orders TradeProto::Account account(1, "Test", "USD", 1000); account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000)); account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100)); account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10)); // Serialize the account to the Protobuf buffer Trade::protobuf::Account output; account.Serialize(output); auto buffer = output.SerializeAsString(); // Show original and Protobuf serialized sizes std::cout << "Original size: " << account.size() << std::endl; std::cout << "Protobuf size: " << buffer.size() << std::endl; // Deserialize the account from the Protobuf buffer Trade::protobuf::Account input; input.ParseFromString(buffer); TradeProto::Account deserialized; deserialized.Deserialize(input); // Show account content std::cout << std::endl; std::cout << "Account.Id = " << deserialized.Id << std::endl; std::cout << "Account.Name = " << deserialized.Name << std::endl; std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl; std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl; for (auto& order : deserialized.Orders) { std::cout << "Account.Order => Id: " << order.Id << ", Symbol: " << order.Symbol << ", Side: " << (int)order.Side << ", Type: " << (int)order.Type << ", Price: " << order.Price << ", Volume: " << order.Volume << std::endl; } // Delete all global objects allocated by Protobuf google::protobuf::ShutdownProtobufLibrary(); return 0; }
Output of the example is the following:
Original size: 128
Protobuf size: 120
Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10
Protobuf serialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.339 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:15:26 2025
UTC timestamp: Wed Jul 16 17:15:26 2025
===============================================================================
Benchmark: Protobuf-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: Protobuf-Serialize
Average time: 322 ns/op
Minimal time: 322 ns/op
Maximal time: 323 ns/op
Total time: 4.989 s
Total operations: 15458464
Total bytes: 2.074 GiB
Operations throughput: 3098338 ops/s
Bytes throughput: 425.503 MiB/s
Custom values:
MessageSize: 120
OriginalSize: 128
===============================================================================
Protobuf deserialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.248 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:16:37 2025
UTC timestamp: Wed Jul 16 17:16:37 2025
===============================================================================
Benchmark: Protobuf-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: Protobuf-Deserialize
Average time: 351 ns/op
Minimal time: 351 ns/op
Maximal time: 352 ns/op
Total time: 4.959 s
Total operations: 14111610
Total bytes: 1.913 GiB
Operations throughput: 2845506 ops/s
Bytes throughput: 390.789 MiB/s
Custom values:
MessageSize: 120
OriginalSize: 128
===============================================================================
SimpleBinaryEncoding serialization
SimpleBinaryEncoding serialization is based on SimpleBinaryEncoding library.
SimpleBinaryEncoding schemaSimpleBinaryEncoding serialization starts with describing a model schema. For our domain model the schema will be the following:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe" package="sbe" id="1" version="1" semanticVersion="5.2" description="Trade schema" byteOrder="littleEndian"> <types> <composite name="messageHeader" description="Message identifiers and length of message root"> <type name="blockLength" primitiveType="uint16"/> <type name="templateId" primitiveType="uint16"/> <type name="schemaId" primitiveType="uint16"/> <type name="version" primitiveType="uint16"/> </composite> <composite name="groupSizeEncoding" description="Repeating group dimensions"> <type name="blockLength" primitiveType="uint16"/> <type name="numInGroup" primitiveType="uint16"/> </composite> <composite name="varStringEncoding"> <type name="length" primitiveType="uint32" maxValue="1073741824"/> <type name="varData" primitiveType="uint8" length="0" characterEncoding="UTF-8"/> </composite> </types> <types> <enum name="OrderSide" encodingType="uint8"> <validValue name="buy">0</validValue> <validValue name="sell">1</validValue> </enum> <enum name="OrderType" encodingType="uint8"> <validValue name="market">0</validValue> <validValue name="limit">1</validValue> <validValue name="stop">2</validValue> </enum> <composite name="Order"> <type name="id" primitiveType="int32"/> <type name="symbol" primitiveType="char" length="10" characterEncoding="UTF-8"/> <ref name="side" type="OrderSide"/> <ref name="type" type="OrderType"/> <type name="price" primitiveType="double"/> <type name="volume" primitiveType="double"/> </composite> <composite name="Balance"> <type name="currency" primitiveType="char" length="10" characterEncoding="UTF-8"/> <type name="amount" primitiveType="double"/> </composite> <type name="AccountId" primitiveType="int32"/> </types> <sbe:message name="Account" id="1"> <field name="id" id="1" type="AccountId"/> <field name="wallet" id="2" type="Balance"/> <group name="orders" id="3" dimensionType="groupSizeEncoding"> <field name="order" id="4" type="Order"/> </group> <data name="name" id="5" type="varStringEncoding"/> </sbe:message> </sbe:messageSchema>SimpleBinaryEncoding schema compilation
The next step is a schema compilation using 'sbe' utility which will create a generated code for required programming language.
The following command will create a C++ generated code:
java -Dsbe.target.language=cpp -jar sbe-all-1.29.0.jar trade.sbe.xml
As the result required C++ header files will be generated.
SimpleBinaryEncoding serialization methodsFinally you should extend your domain model with a SimpleBinaryEncoding serialization methods:
#include "fbe/trade_models.h" #include <algorithm> namespace TradeProto { struct Order { ... // SimpleBinaryEncoding serialization void Serialize(sbe::Order& model) { model.id(Id); model.putSymbol(Symbol); model.side((sbe::OrderSide::Value)Side); model.type((sbe::OrderType::Value)Type); model.price(Price); model.volume(Volume); } void Deserialize(sbe::Order& model) { Id = model.id(); model.getSymbol(Symbol, sizeof(Symbol)); Side = (OrderSide)model.side(); Type = (OrderType)model.type(); Price = model.price(); Volume = model.volume(); } ... }; struct Balance { ... // SimpleBinaryEncoding serialization void Serialize(sbe::Balance& model) { model.putCurrency(Currency); model.amount(Amount); } void Deserialize(sbe::Balance& model) { model.getCurrency(Currency, sizeof(Currency)); Amount = model.amount(); } ... }; struct Account { ... // SimpleBinaryEncoding serialization void Serialize(sbe::Account& model) { model.id(Id); model.putName(Name); Wallet.Serialize(model.wallet()); auto orders = model.ordersCount((uint16_t)Orders.size()); for (auto& order : Orders) order.Serialize(orders.next().order()); } void Deserialize(sbe::Account& model) { Id = model.id(); Name = model.getNameAsString(); Wallet.Deserialize(model.wallet()); Orders.clear(); auto orders = model.orders(); for (int i = 0; i < orders.count(); ++i) { Order order; order.Deserialize(orders.next().order()); Orders.emplace_back(order); } } ... }; } // namespace TradeProtoSimpleBinaryEncoding example
Here comes the usage example of SimpleBinaryEncoding serialize/deserialize functionality:
#include "../proto/trade.h" #include <iostream> int main(int argc, char** argv) { // Create a new account with some orders TradeProto::Account account(1, "Test", "USD", 1000); account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000)); account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100)); account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10)); // Serialize the account to the SBE buffer char buffer[1024]; sbe::MessageHeader header; header.wrap(buffer, 0, 1, sizeof(buffer)) .blockLength(sbe::Account::sbeBlockLength()) .templateId(sbe::Account::sbeTemplateId()) .schemaId(sbe::Account::sbeSchemaId()) .version(sbe::Account::sbeSchemaVersion()); sbe::Account message; message.wrapForEncode(buffer, header.encodedLength(), sizeof(buffer)); account.Serialize(message); // Show original and SBE serialized sizes std::cout << "Original size: " << account.size() << std::endl; std::cout << "SBE size: " << header.encodedLength() + message.encodedLength() << std::endl; // Deserialize the account from the SBE buffer header.wrap(buffer, 0, 1, sizeof(buffer)); int actingVersion = header.version(); int actingBlockLength = header.blockLength(); message.wrapForDecode(buffer, header.encodedLength(), actingBlockLength, actingVersion, sizeof(buffer)); TradeProto::Account deserialized; deserialized.Deserialize(message); // Show account content std::cout << std::endl; std::cout << "Account.Id = " << deserialized.Id << std::endl; std::cout << "Account.Name = " << deserialized.Name << std::endl; std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl; std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl; for (auto& order : deserialized.Orders) { std::cout << "Account.Order => Id: " << order.Id << ", Symbol: " << order.Symbol << ", Side: " << (int)order.Side << ", Type: " << (int)order.Type << ", Price: " << order.Price << ", Volume: " << order.Volume << std::endl; } return 0; }
Output of the example is the following:
Original size: 128
SBE size: 138
Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10
SimpleBinaryEncoding performance
SimpleBinaryEncoding serialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.311 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:17:49 2025
UTC timestamp: Wed Jul 16 17:17:49 2025
===============================================================================
Benchmark: SimpleBinaryEncoding-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: SimpleBinaryEncoding-Serialize
Average time: 35 ns/op
Minimal time: 35 ns/op
Maximal time: 35 ns/op
Total time: 5.926 s
Total operations: 168709597
Total bytes: 26.020 GiB
Operations throughput: 28467195 ops/s
Bytes throughput: 4.399 GiB/s
Custom values:
MessageSize: 138
OriginalSize: 128
===============================================================================
SimpleBinaryEncoding deserialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.261 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:19:22 2025
UTC timestamp: Wed Jul 16 17:19:22 2025
===============================================================================
Benchmark: SimpleBinaryEncoding-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: SimpleBinaryEncoding-Deserialize
Average time: 52 ns/op
Minimal time: 52 ns/op
Maximal time: 52 ns/op
Total time: 5.432 s
Total operations: 104359134
Total bytes: 16.097 GiB
Operations throughput: 19211002 ops/s
Bytes throughput: 2.985 GiB/s
Custom values:
MessageSize: 138
OriginalSize: 128
===============================================================================
zpp::bits serialization is based on zpp::bits library.
zpp::bits serialization methodsFinally you should extend your domain model with a zpp::bits serialization methods:
#include "fbe/trade_models.h" #include <algorithm> namespace TradeProto { struct Order { ... // zpp::bits serialization using serialize = zpp::bits::members<6>; ... }; struct Balance { ... // zpp::bits serialization using serialize = zpp::bits::members<2>; ... }; struct Account { ... // zpp::bits serialization using serialize = zpp::bits::members<4>; ... }; } // namespace TradeProto
Here comes the usage example of zpp::bits serialize/deserialize functionality:
#include "../proto/trade.h" #include <iostream> int main(int argc, char** argv) { // Create a new account with some orders TradeProto::Account account(1, "Test", "USD", 1000); account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000)); account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100)); account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10)); // Serialize the account to the zpp::bits buffer auto [buffer, out] = zpp::bits::data_out(); (void) out(account); // Show original and zpp::bits serialized sizes std::cout << "Original size: " << account.size() << std::endl; std::cout << "zpp::bits size: " << buffer.size() << std::endl; // Deserialize the account from the zpp::bits buffer TradeProto::Account deserialized; (void) zpp::bits::in{buffer}(deserialized); // Show account content std::cout << std::endl; std::cout << "Account.Id = " << deserialized.Id << std::endl; std::cout << "Account.Name = " << deserialized.Name << std::endl; std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl; std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl; for (const auto& order : deserialized.Orders) { std::cout << "Account.Order => Id: " << order.Id << ", Symbol: " << order.Symbol << ", Side: " << (int)order.Side << ", Type: " << (int)order.Type << ", Price: " << order.Price << ", Volume: " << order.Volume << std::endl; } return 0; }
Output of the example is the following:
Original size: 128
zpp::bits size: 130
Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10
zpp::bits serialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.142 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:38:54 2025
UTC timestamp: Wed Jul 16 17:38:54 2025
===============================================================================
Benchmark: zpp::bits-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: zpp::bits-Serialize
Average time: 34 ns/op
Minimal time: 33 ns/op
Maximal time: 35 ns/op
Total time: 5.790 s
Total operations: 168487238
Total bytes: 24.490 GiB
Operations throughput: 29095978 ops/s
Bytes throughput: 4.232 GiB/s
Custom values:
MessageSize: 130
OriginalSize: 128
===============================================================================
zpp::bits deserialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.159 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:39:52 2025
UTC timestamp: Wed Jul 16 17:39:52 2025
===============================================================================
Benchmark: zpp::bits-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: zpp::bits-Deserialize
Average time: 37 ns/op
Minimal time: 36 ns/op
Maximal time: 37 ns/op
Total time: 5.652 s
Total operations: 152512801
Total bytes: 22.161 GiB
Operations throughput: 26982853 ops/s
Bytes throughput: 3.942 GiB/s
Custom values:
MessageSize: 130
OriginalSize: 128
===============================================================================
JSON serialization is based on RapidJSON library.
JSON serialization methodsFinally you should extend your domain model with a JSON serialization methods:
#include "serialization/json/serializer.h" #include "serialization/json/deserializer.h" namespace TradeProto { struct Order { ... // JSON serialization template<typename OutputStream> void Serialize(CppSerialization::JSON::Serializer<OutputStream>& serializer) { serializer.StartObject(); serializer.Pair("id", Id); serializer.Pair("symbol", Symbol); serializer.Pair("side", (int)Side); serializer.Pair("type", (int)Type); serializer.Pair("price", Price); serializer.Pair("volume", Volume); serializer.EndObject(); } template<typename JSON> void Deserialize(const JSON& json) { using namespace CppSerialization::JSON; Deserializer::Find(json, "id", Id); Deserializer::Find(json, "symbol", Symbol); int side = 0; Deserializer::Find(json, "side", side); Side = (OrderSide)side; int type = 0; Deserializer::Find(json, "type", type); Type = (OrderType)type; Deserializer::Find(json, "price", Price); Deserializer::Find(json, "volume", Volume); } ... }; struct Balance { ... // JSON serialization template<typename OutputStream> void Serialize(CppSerialization::JSON::Serializer<OutputStream>& serializer) { serializer.StartObject(); serializer.Pair("currency", Currency); serializer.Pair("amount", Amount); serializer.EndObject(); } template<typename JSON> void Deserialize(const JSON& json) { using namespace CppSerialization::JSON; Deserializer::Find(json, "currency", Currency); Deserializer::Find(json, "amount", Amount); } ... }; struct Account { ... // JSON serialization template<typename OutputStream> void Serialize(CppSerialization::JSON::Serializer<OutputStream>& serializer) { serializer.StartObject(); serializer.Pair("id", Id); serializer.Pair("name", Name); serializer.Key("wallet"); Wallet.Serialize(serializer); serializer.Key("orders"); serializer.StartArray(); for (auto& order : Orders) order.Serialize(serializer); serializer.EndArray(); serializer.EndObject(); } template<typename JSON> void Deserialize(const JSON& json) { using namespace CppSerialization::JSON; Deserializer::Find(json, "id", Id); Deserializer::Find(json, "name", Name); Deserializer::FindObject(json, "wallet", [this](const Value::ConstObject& object) { Wallet.Deserialize(object); }); Orders.clear(); Deserializer::FindArray(json, "orders", [this](const Value& item) { Order order; order.Deserialize(item); Orders.emplace_back(order); }); } ... }; } // namespace TradeProto
Here comes the usage example of JSON serialize/deserialize functionality:
#include "../proto/trade.h" #include "serialization/json/parser.h" #include <iostream> int main(int argc, char** argv) { // Create a new account with some orders TradeProto::Account account(1, "Test", "USD", 1000); account.Orders.emplace_back(TradeProto::Order(1, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::MARKET, 1.23456, 1000)); account.Orders.emplace_back(TradeProto::Order(2, "EURUSD", TradeProto::OrderSide::SELL, TradeProto::OrderType::LIMIT, 1.0, 100)); account.Orders.emplace_back(TradeProto::Order(3, "EURUSD", TradeProto::OrderSide::BUY, TradeProto::OrderType::STOP, 1.5, 10)); // Serialize the account to the JSON buffer CppSerialization::JSON::StringBuffer buffer; CppSerialization::JSON::Serializer<CppSerialization::JSON::StringBuffer> serializer(buffer); account.Serialize(serializer); // Show original and JSON serialized sizes std::cout << "Original size: " << account.size() << std::endl; std::cout << "JSON content: " << buffer.GetString() << std::endl; std::cout << "JSON size: " << buffer.GetSize() << std::endl; // Parse JSON string CppSerialization::JSON::Document json = CppSerialization::JSON::Parser::Parse(buffer.GetString()); // Deserialize the account from the JSON buffer TradeProto::Account deserialized; deserialized.Deserialize(json); // Show account content std::cout << std::endl; std::cout << "Account.Id = " << deserialized.Id << std::endl; std::cout << "Account.Name = " << deserialized.Name << std::endl; std::cout << "Account.Wallet.Currency = " << deserialized.Wallet.Currency << std::endl; std::cout << "Account.Wallet.Amount = " << deserialized.Wallet.Amount << std::endl; for (auto& order : deserialized.Orders) { std::cout << "Account.Order => Id: " << order.Id << ", Symbol: " << order.Symbol << ", Side: " << (int)order.Side << ", Type: " << (int)order.Type << ", Price: " << order.Price << ", Volume: " << order.Volume << std::endl; } return 0; }
Output of the example is the following:
Original size: 128
JSON content: {"id":1,"name":"Test","wallet":{"currency":"USD","amount":1000.0},"orders":[{"id":1,"symbol":"EURUSD","side":0,"type":0,"price":1.23456,"volume":1000.0},{"id":2,"symbol":"EURUSD","side":1,"type":1,"price":1.0,"volume":100.0},{"id":3,"symbol":"EURUSD","side":0,"type":2,"price":1.5,"volume":10.0}]}
JSON size: 297
Account.Id = 1
Account.Name = Test
Account.Wallet.Currency = USD
Account.Wallet.Amount = 1000
Account.Order => Id: 1, Symbol: EURUSD, Side: 0, Type: 0, Price: 1.23456, Volume: 1000
Account.Order => Id: 2, Symbol: EURUSD, Side: 1, Type: 1, Price: 1, Volume: 100
Account.Order => Id: 3, Symbol: EURUSD, Side: 0, Type: 2, Price: 1.5, Volume: 10
JSON serialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.041 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:29:18 2025
UTC timestamp: Wed Jul 16 17:29:18 2025
===============================================================================
Benchmark: JSON-Serialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: JSON-Serialize
Average time: 696 ns/op
Minimal time: 695 ns/op
Maximal time: 710 ns/op
Total time: 4.932 s
Total operations: 7078563
Total bytes: 2.357 GiB
Operations throughput: 1435099 ops/s
Bytes throughput: 487.794 MiB/s
Custom values:
MessageSize: 297
OriginalSize: 128
===============================================================================
JSON document parsing performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.154 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:30:48 2025
UTC timestamp: Wed Jul 16 17:30:48 2025
===============================================================================
Benchmark: JSON-Parse
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: JSON-Parse
Average time: 771 ns/op
Minimal time: 771 ns/op
Maximal time: 779 ns/op
Total time: 4.914 s
Total operations: 6372779
Total bytes: 2.118 GiB
Operations throughput: 1296734 ops/s
Bytes throughput: 440.764 MiB/s
Custom values:
MessageSize: 297
===============================================================================
JSON deserialization performance of the provided domain model is the following:
===============================================================================
CppBenchmark report. Version 1.0.5.0
===============================================================================
CPU architecture: Apple M1 Pro
CPU logical cores: 10
CPU physical cores: 10
CPU clock speed: 2.400 GHz
CPU Hyper-Threading: disabled
RAM total: 32.000 GiB
RAM free: 1.240 GiB
===============================================================================
OS version: 24.5.0
OS bits: 64-bit
Process bits: 64-bit
Process configuration: release
Local timestamp: Wed Jul 16 19:31:50 2025
UTC timestamp: Wed Jul 16 17:31:50 2025
===============================================================================
Benchmark: JSON-Deserialize
Attempts: 5
Duration: 5 seconds
-------------------------------------------------------------------------------
Phase: JSON-Deserialize
Average time: 291 ns/op
Minimal time: 291 ns/op
Maximal time: 292 ns/op
Total time: 4.924 s
Total operations: 16884318
Total bytes: 77.297 MiB
Operations throughput: 3428637 ops/s
Bytes throughput: 15.711 MiB/s
Custom values:
MessageSize: 297
OriginalSize: 128
===============================================================================
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4