Skip to content

Repository files navigation

ProtoCache

ProtoCache is an alternative flat binary format for Protobuf schemas. Like FlatBuffers, it supports direct reads without first materializing an object graph, while usually producing smaller data and supporting maps. The benchmark compares data size, traversal, reflection, and compression costs. ProtoCache is designed for workloads that need a balance between compact cached data and fast reads.

ProtobufProtoCacheFlatBuffersCap'n ProtoFory
Data Size574B780B1296B1288B615B
Decode + Traverse + Dealloc2620ns132ns89ns610ns1748ns
Decode + Traverse(reflection) + Dealloc8189ns270ns484ns8748ns-
Compressed/Packed Size566B571B856B626B611B
Compress258ns427ns775ns-295ns
Decompress/Unpack114ns229ns532ns653ns140ns

A naive compress algorithm is introduced to reduce continuous 0x00 or 0xff bytes, which makes the final output size of ProtoCache close to Protobuf. Because Cap'n Proto has a builtin pack algorithm, which shows better compress ratio than our naive compress algorithm, without explicit compress/decompress API, we take the time gap between access in plain and packed mode as decompress time.

Difference to Protobuf

ProtoCache reserves a single repeated field named _ with field number 1 as an alias to an array or map rather than a normal one-field message. This makes multidimensional containers possible without an extra message layer:

messageVec2D {
messageVec1D {
repeatedfloat_=1;
}
repeatedVec1D_=1;
}

For all other schema and object-model differences, including field numbering, presence and defaults, oneof, maps, deprecated declarations, unknown fields, and evolution rules, see schema.md.

Build and Install

ProtoCache requires a C++17 compiler and Protobuf. Tests additionally require GoogleTest, while command-line tools require gflags. CMake 3.13 or newer is required.

cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DWITH_TEST=ON \
-DWITH_TOOLS=ON
cmake --build build
ctest --test-dir build --output-on-failure
cmake --install build --prefix /path/to/prefix

WITH_BENCHMARK and PROTOCACHE_ENABLE_NATIVE_OPT are disabled by default so normal builds remain portable. Benchmark builds enable native CPU optimization and require the additional benchmark libraries used by this repository. PROTOCACHE_BUILD_SHARED is enabled by default on Linux and macOS and disabled on Windows. Windows currently supports the static libraries, tools, and Python extension; building the shared library there is not supported yet.

Installed CMake packages can be consumed directly:

find_package(ProtoCache1.2CONFIGREQUIRED)
target_link_libraries(my_targetPRIVATEProtoCache::protocache)

Available library targets are ProtoCache::protocache (full static library) and ProtoCache::protocache-lite (static core library without the reflection extension). When PROTOCACHE_BUILD_SHARED=ON, the install also provides ProtoCache::protocache-shared on Linux and macOS. When tools are enabled, the install contains the JSON converters and all protoc plugins.

Format compatibility is governed by data-format.md.

Code Gen

protoc --pccx_out=. [--pccx_opt=extra] test.proto

A protobuf compiler plugin called protoc-gen-pccx is available to generate header-only C++ file. If option extra is set, it will generate another file for extra APIs.

APIs

protocache::Buffer buf1;
ASSERT_TRUE(protocache::Serialize(pb_message, &buf1));
// =========basic api=========auto& root = protocache::Message(buf1.View()).Cast<test::Main>();
ASSERT_FALSE(!root);
// =========extra api=========
::ex::test::Main ex_root(buf1.View());
protocache::Buffer buf2;
ASSERT_TRUE(ex_root.Serialize(&buf2));
ASSERT_EQ(buf1.Size(), buf2.Size());
// deserialize to pbprotocache::Deserialize(data, &pb_mirror);

You can create protocache binary by serializing a protobuf message with protocache::Serialize. The Basic API offers fast read-only access with zero-copy technique. Extra APIs provide a mutable object and another serialization method, which only reserialize accessed parts.

ProtobufProtoCacheEXProtoCache
Serialize557ns308 ~ 1879ns6493ns
Decode + Traverse + Dealloc2620ns1227ns132ns
Serialize (twitter.proto)215us101us412us

Test full serialization with a complicated twitter.proto. Since serializing big object is a memory-bound task, ProtoCache may show it's advantage.

Reflection

std::string err;
google::protobuf::FileDescriptorProto file;
ASSERT_TRUE(protocache::ParseProtoFile("test.proto", &file, &err));
protocache::reflection::DescriptorPool pool;
ASSERT_TRUE(pool.Register(file));
auto descriptor = pool.Find("test.Main");
ASSERT_NE(descriptor, nullptr);

The reflection apis are simliar to protobuf's. An example can be found in the test. If you don't need reflection including the basic serialize API, linking protocache-lite instead of protocache library to avoid dependency on protobuf may be a good idea.

Other Implements

LanguageSource
Pythonpython
TypeScripttypescript
Gohttps://github.com/peterrk/protocache-go
Javahttps://github.com/peterrk/protocache-java
C#https://github.com/peterrk/protocache.net
Rusthttps://github.com/peterrk/protocache-rust
Swifthttps://github.com/peterrk/protocache-swift

Data Size Evaluation

Following work in paper, we can find that protocache has smaller data size than FlatBuffers and Cap'n Proto, in most cases.

ProtobufProtoCacheFlatBuffersCap'n ProtoProtoCache (Packed)Cap'n Proto (Packed)
CircleCI Definition (Blank)58202466
CircleCI Matrix Definition2688104965036
Entry Point Regulation Manifest247352504536303318
ESLint Configuration Document161276320216175131
ECMAScript Module Loader Definition234480803335
GeoJSON Example Document325432680448250228
GitHub FUNDING Sponsorship Definition (Empty)172468402325
GitHub Workflow Definition189288440464237242
Grunt.js Clean Task Definition2048116962839
ImageOptimizer Azure Webjob Configuration2360100964044
JSON-e Templating Engine Reverse Sort Example21681362403843
JSON-e Templating Engine Sort Example103644482118
JSON Feed Example Document413484584568474470
JSON Resume Example222526083116315225372549
.NET Core Project284328636608303376
OpenWeatherMap API Example Document188244384320199206
OpenWeather Road Risk API Example173240328296205204
NPM Package.json Example Manifest158117362268221617261755
TravisCI Notifications Configuration521600668640601566
TSLint Linter Definition (Basic)82460481412
TSLint Linter Definition (Extends Only)476888886262
TSLint Linter Definition (Multi-rule)143284802023

About

Alternative flat binary format for Protobuf schema

Topics

Resources

Stars

83 stars

Watchers

8 watching

Forks

Releases

Packages

Contributors

Languages