Home

Awesome

CPP serializers benchmark

tested libraries

GCC 11 (Ubuntu 20.04 x64)

librarytest casebin sizedata sizeser timedes time
bitserygeneral70904B6913B1470ms1524ms
bitserybrief syntax<sup>1</sup>70888B6913B1416ms1561ms
bitserycompatibility<sup>2</sup>75192B7113B1490ms1291ms
bitserycompression<sup>3</sup>70848B4213B1927ms2044ms
bitseryfixed buffer<sup>4</sup>53648B6913B927ms1466ms
bitserystream<sup>5</sup>59568B6913B1611ms6180ms
bitseryunsafe read<sup>6</sup>66760B6913B1352ms982ms
boostgeneral279024B11037B15126ms12724ms
cerealgeneral70560B10413B10777ms9088ms
flatbuffersgeneral70640B14924B8757ms3361ms
handwrittengeneral<sup>7</sup>47936B10413B1506ms1577ms
handwrittenunsafe<sup>8</sup>47944B10413B1616ms1392ms
iostreamgeneral<sup>9</sup>53872B8413B11956ms12928ms
msgpackgeneral89144B8857B2770ms14033ms
protobufgeneral2077864B10018B19929ms20592ms
protobufarena<sup>10</sup>2077872B10018B10319ms11787ms
yasgeneral<sup>11</sup>61072B10463B2286ms1770ms
yascompression<sup>12</sup>65400B7315B2770ms2498ms
yasstream<sup>13</sup>56184B10463B10871ms11182ms
zpp_bitsgeneral52192B8413B733ms693ms
zpp_bitsfixed buffer48000B8413B620ms667ms

Clang 12.0.1 (Ubuntu 20.04 x64)

librarytest casebin sizedata sizeser timedes time
bitserygeneral53728B6913B2128ms1832ms
bitserybrief syntax<sup>1</sup>55320B6913B2789ms2071ms
bitserycompatibility<sup>2</sup>54360B7113B2195ms1953ms
bitserycompression<sup>3</sup>54688B4213B4315ms4181ms
bitseryfixed buffer<sup>4</sup>49248B6913B946ms1941ms
bitserystream<sup>5</sup>54776B6913B2047ms6089ms
bitseryunsafe read<sup>6</sup>49688B6913B2092ms1162ms
boostgeneral237008B11037B16011ms13017ms
cerealgeneral61480B10413B9977ms8565ms
flatbuffersgeneral62512B14924B9812ms3472ms
handwrittengeneral<sup>7</sup>43112B10413B1391ms1321ms
handwrittenunsafe<sup>8</sup>43120B10413B1393ms1212ms
iostreamgeneral<sup>9</sup>48632B8413B10992ms12771ms
msgpackgeneral77384B8857B3563ms14705ms
protobufgeneral2032712B10018B18125ms20211ms
protobufarena<sup>10</sup>2032760B10018B9166ms11378ms
yasgeneral<sup>11</sup>51000B10463B2114ms1558ms
yascompression<sup>12</sup>51424B7315B2874ms2739ms
yasstream<sup>13</sup>54680B10463B10624ms10604ms
zpp_bitsgeneral47128B8413B790ms715ms
zpp_bitsfixed buffer43056B8413B605ms694ms

Additional tests information

  1. deserialization using brief\_syntax, similar to cereal
  2. forward/backward compatibility enabled for Monster
  3. all components of Vec3 is compressed in [-1.0, 1.0] range with precision 0.01
  4. use non-resizable buffer uint8_t[150000] for serialization
  5. use stream input/output adapter, underlying type is std::stringstream
  6. on deserialization do not check for errors
  7. check buffer size on reading, but writing buffer is preallocated std::array<uint8_t, 1000000>
  8. doesn't check for buffer size when reading, buffer: std::array<uint8_t, 1000000>
  9. use std::stringstream's internal std::string
  10. use arena allocator
  11. use yas::mem_<io>stream as buffer
  12. with yas::no_header and yas::compacted
  13. using std::stringstream

NOTE: tests for protobuf and flatbuffers is not 100% fair, because huge amount of CPU cycles goes to converting from generated types, to our defined types.

Why another cpp serializers benchmark

I'm aware that cpp-serializers project already exists, but it's testing set is way too simple and you cannot compile each project to separate executable.

This project contains more realisting data that needs to be serialized.

    enum Color : uint8_t {
        Red,
        Green,
        Blue
    };

    struct Vec3 {
        float x;
        float y;
        float z;
    };

    struct Weapon {
        std::string name;
        int16_t damage;
    };

    struct Monster {
        Vec3 pos;
        int16_t mana;
        int16_t hp;
        std::string name;
        std::vector<uint8_t> inventory;
        Color color;
        std::vector<Weapon> weapons;
        Weapon equipped;
        std::vector<Vec3> path;
    };

All data is random generated, although seed is hard-coded to get predictable results when running same test multiple times.

All projects implement same interface for serialization and deserialization.

struct Buf {
    const uint8_t* ptr;
    size_t bytesCount;
};

class ISerializerTest {
public:
    virtual Buf serialize(const std::vector<MyTypes::Monster>& data) = 0;
    virtual void deserialize(Buf buf, std::vector<MyTypes::Monster>& res) = 0;
    virtual ~ISerializerTest() = default;
};

Testing routine consist of few steps:

Building & testing

  1. Build project
    mkdir build && cd build
    cmake ..
    make
    
  2. Run tests with ctest -VV OR
  3. Generate testing results (requires nodejs)
    cd ../tools/
    npm install
    npm start