Yandex Open-Sources YaFF: A Zero-Copy Wire Format for Protobuf With Near-Struct Read Speed
TLDR
- YaFF is Yandex’s open-source zero-copy wire format for Protobuf — Apache 2.0, at the moment C++, v0.1.0.
- The .proto file stays the supply of reality; solely the bodily reminiscence format adjustments.
- On Yandex’s benchmarks, the Flat Layout reads scorching knowledge ~3.8× sooner than FlatBuffers, inside 1.2× of a uncooked C++ struct.
- Four layouts — Fixed, Flat, Sparse, Dynamic — commerce learn velocity for schema flexibility; Dynamic is the default.
- YaFF runs in its promoting suggestion system, the place it stories 10–20% CPU financial savings at manufacturing scale.
- Adoption is incremental: drop it into one scorching path, with two-way Protobuf conversion on the edges.
Yandex has open-sourced YaFF (Yet another Flat Format) below Apache 2.0. It is a high-performance C++ serialization library. YaFF supplies a zero-copy wire format for the Protobuf ecosystem. Your .proto file stays the only supply of reality. The format solely adjustments how knowledge sits in reminiscence. It concentrates on server-side runtimes.
What is YaFF
YaFF is just not a alternative for Protobuf. It is another wire format for Protobuf messages. The similar .proto schema generates a proto-like C++ API. Reads want no parsing step, so fields come straight from the buffer. Less performance-sensitive code can nonetheless parse the wire format again into Protobuf messages. That two-way conversion is what makes module-by-module adoption reasonable. You introduce YaFF in a single scorching path and depart the remaining on Protobuf.
The Problem it Targets
Protobuf parsing can eat double-digit percentages of CPU in high-load backends. At scale, that maps to 1000’s of bodily cores. The widespread zero-copy choice is FlatBuffers, additionally from Google. But FlatBuffers is just not a Protobuf drop-in and requires sustaining a separate schema and conversion layer. semantically incompatible with Protobuf. Migrating means duplicated schemas, totally different schema-evolution guidelines , and hand-written discipline converters. Many groups conclude the associated fee is just not value it. YaFF goals at that hole: zero-copy reads with Protobuf semantics preserved.
How the Layouts Work
A format decides how a message is saved within the buffer. It adjustments solely the bodily illustration, leaving the schema and generated interfaces unchanged. YaFF ships 4 layouts. Fixed is a plain packed struct with no header and a frozen schema. Flat provides a two-byte header and helps schema evolution. Sparse addresses fields by means of a meta desk, becoming sparse schemas. Dynamic is the default and selects Flat or Sparse at runtime. It makes use of Flat whereas the schema permits, then switches to Sparse when evolution breaks flat alignment.
| Layout | Read entry | Per-message overhead | Schema evolution | Best for |
| Fixed | 1 learn, 0 branches | 0 bytes | Frozen | Small inlined primitives |
| Flat | 2 reads, 1 department | 2 bytes | Restricted (sort preservation) | Dense, scorching knowledge |
| Sparse | 4 reads, 2 branches | 6 bytes | Unrestricted | Sparse schemas, free evolution |
| Dynamic (default) | Flat or Sparse at runtime | 2 or 6 bytes | Unrestricted | General software logic |
Benchmark
Yandex ships a reproducible benchmark suite, constructed with google/benchmark in a Release construct. The numbers beneath are median nanoseconds per learn on an AMD EPYC 7713 with Clang 20.1.8. Lower is quicker. In the recent hierarchical case, the Flat Layout reads in 9.79 ns. FlatBuffers wants 37.30 ns, and Protobuf wants 219.35 ns. The uncooked C++ struct baseline is 8.14 ns. So the Flat Layout reads about 3.8× sooner than FlatBuffers right here, and about 22× sooner than Protobuf. It stays inside 1.2× of the uncooked struct.
| Format | Read time (ns) | Slowdown vs uncooked struct |
| Raw C++ struct | 8.14 | 1.0× |
| YaFF Flat Layout | 9.79 | 1.2× |
| YaFF Sparse Layout | 21.23 | 2.6× |
| FlatBuffers | 37.30 | 4.6× |
| Protobuf | 219.35 | 26.9× |
Note: The absolute numbers rely on the host CPU and reminiscence. The ratios between codecs are anticipated to carry throughout {hardware}.
The Compiler Aliasing Detail
FlatBuffers and YaFF each learn fields by reinterpreting uncooked reminiscence because the goal sort. That type-punning leaves TBAA with out robust sufficient information. So LLVM’s alias evaluation falls again to a conservative MayAlias verdict. The compiler then can’t show that repeated accesses are protected to reuse. Writing root.intermediate().leaf().a() twice re-walks the tree every time. YaFF provides annotations in its generated code that inform the compiler when reuse is protected. YaFF’s generated-code annotations can typically assist the compiler reuse the entry chain, so long as the related reminiscence is just not modified between reads. As lengthy as nothing writes to reminiscence between reads, YaFF caches the entry chain by itself.
Where It Fits: Use Cases
YaFF targets techniques the place you management each producer and shopper. Recommendation and ad-serving backends are the clearest match. According to Yandex, YaFF runs in its promoting suggestion system, the place it stories 10–20% CPU financial savings at manufacturing scale. Memory-mapped indexes are a second match. A host can maintain tens of gigabytes of native knowledge. Those mmap-able indexes survive service restarts with out re-parsing. Search indexes, characteristic shops, and feed providers share that read-heavy profile. The deliberate Columnar Layout targets analytics and ML pipelines with giant repeated fields. YaFF may also be extra compact than FlatBuffers, which helps cache conduct.
A Look on the Code
The learn path mirrors Protobuf, minus the parse step.
#embody "feed.pb.h" // generated by protoc
#embody "feed.yaff.h" // generated by yaff_generate()
// 1. Serialize an present Protobuf message right into a YaFF buffer.
feed::FeedResponse proto = LoadFeedResponse();
const auto buffer = yaff::Serialize<protoyaff::feed::FeedResponse>(proto);
// 2. Read fields immediately from the buffer. There is not any parsing step.
const auto& response = yaff::ReadMessage<protoyaff::feed::FeedResponse>(buffer.Data());
for (const auto& merchandise : response.gadgets()) {
std::string_view title = merchandise.title();
std::string_view creator = merchandise.creator().identify(); // empty if creator is unset
}
// 3. Convert again to Protobuf when a shopper wants the parsed message.
feed::FeedResponse restored;
response.ParseTo(restored);
You add YaFF by means of CMake (find_package) or Conan. Code era runs protobuf_generate() then yaff_generate(). Generated YaFF varieties reside within the protoyaff::<bundle> namespace. Most initiatives solely hyperlink yaff::core and yaff::proto.
Resources:
Check out the GitHub repository and Documentation.
The submit Yandex Open-Sources YaFF: A Zero-Copy Wire Format for Protobuf With Near-Struct Read Speed appeared first on MarkTechPost.
