Live data from Hacker News

Don't Use Protobuf for Telemetry

richardstartin.github.io

71–80 of 195 posts

Re: Don't Use Protobuf for Telemetry

#71
post #51

Earlier quoted context omitted.

Why they use Java like code in Python? "MessageToJson" doesn't look idiomatic.

This is because this follows Google’s internal Python styeguide, which was established well before PEP8 and even Python 2.x.

Notably this is no longer true[0]. While legacy stuff can still use the CapWords styling, snake_case names are preferred for all new methods and functions (including code that wraps c++). But this is a relatively recent change to the style guide, and the proto libraries far predate it.

(As a response to a sibling of the parent, the same rules apply to internal code, CapWords should only be used for consistency within a file. All new code should be embrace it's snakey heritage).

[0]: https://google.github.io/styleguide/pyguide.html#3162-naming...

Re: Don't Use Protobuf for Telemetry

#72
post #31

I have written an in-house implementation of protobuf for C++ (sorry can't share) and studied the wire format extensively. Google's implementations, at least C++ and Java, are a bunch of bloated crap (or maybe they're very good, but for a use case that I haven't yet encountered). Don't shoot down the format because of a specific implementation, find or write a better one and enjoy the fact that every language has at…

If you truly wanted speed, why not Cap'n'Proto?

Re: Don't Use Protobuf for Telemetry

#73
post #46

Earlier quoted context omitted.

The author of this piece is wanting to stream possibly multi-MB messages with sub-linear memory use. See more in our Twitter conversation here: https://twitter.com/richardstartin/status/134406813297822105...

"possibly multi-MB messages" Megabytes? By "telemetry" does he mean "getting info on what our pumps out in the oil field are doing" or "snooping on users of a program"?

People tend to jump from telemetry straight to snooping on users even though it's just one possible case.

A possible megabyte of data: A rolling in-memory debug-level log + stack traces with context + timing information, that you snapshot when the app catches and exception. Or a buffered set of timing stats from a minute of runtime.

The author mentioned implementing ddsketch for probabilistic quantile distribution. That's something you run on your own app/infrastructure rather than for snooping on users.

Re: Don't Use Protobuf for Telemetry

#74
post #63

Earlier quoted context omitted.

> Google's implementations, at least C++ and Java, are a bunch of bloated crap (or maybe they're very good, but for a use case that I haven't yet encountered). As someone who has been working on protobuf-related things for >10 years, including creating a size-focused implementation ( https://github.com/protocolbuffers/upb ), and has been working on the protobuf team for >5 years, I have a few thoughts on this (though…

What is this thing with JSON support? Don't people use pb so they do not have to deal with JSON? I'd expect that for a truly lean pb implementation, adding JSON is a 300% increase in code size?

> Don't people use pb so they do not have to deal with JSON?

I think that's one reason, but there are others too, like getting generated type-safe accessors and an explicit schema. I've sometimes used protobuf for this reason even when I'm not planning to use binary format.

> I'd expect that for a truly lean pb implementation, adding JSON is a 300% increase in code size?

Here is a breakdown of a binary that uses upb and links in binary format, JSON format, text format, and some generated protos:

         VM SIZE
     ---------------
      20.6%  26.5Ki    Generated Reflection
        45.7%  12.1Ki    bazel-out/k8-opt/bin/external/com_google_protobuf/src/google/protobuf/test_messages_proto3.upbdefs.c
        39.3%  10.4Ki    bazel-out/k8-opt/bin/external/com_google_protobuf/src/google/protobuf/test_messages_proto2.upbdefs.c
         5.0%  1.33Ki    bazel-out/k8-opt/bin/external/com_google_protobuf/conformance/conformance.upbdefs.c
         3.1%     832    bazel-out/k8-opt/bin/external/com_google_protobuf/google/protobuf/struct.upbdefs.c
         2.4%     648    bazel-out/k8-opt/bin/external/com_google_protobuf/google/protobuf/wrappers.upbdefs.c
         1.2%     320    bazel-out/k8-opt/bin/external/com_google_protobuf/google/protobuf/timestamp.upbdefs.c
         1.2%     312    bazel-out/k8-opt/bin/external/com_google_protobuf/google/protobuf/duration.upbdefs.c
         1.1%     304    bazel-out/k8-opt/bin/external/com_google_protobuf/google/protobuf/field_mask.upbdefs.c
         1.1%     296    [1 Others]
      19.1%  24.5Ki    JSON
        60.4%  14.8Ki    upb/json_decode.c
        39.6%  9.67Ki    upb/json_encode.c
      14.7%  18.9Ki    Reflection Runtime
        81.9%  15.4Ki    upb/def.c
        18.1%  3.41Ki    upb/reflection.c
      13.9%  17.8Ki    Generated Code
        46.9%  8.34Ki    bazel-out/k8-opt/bin/external/com_google_protobuf/google/protobuf/wrappers.upb.c
        17.6%  3.12Ki    bazel-out/k8-opt/bin/external/com_google_protobuf/src/google/protobuf/test_messages_proto3.upb.c
        16.4%  2.92Ki    bazel-out/k8-opt/bin/external/com_google_protobuf/src/google/protobuf/test_messages_proto2.upb.c
        14.7%  2.61Ki    bazel-out/k8-opt/bin/external/com_google_protobuf/google/protobuf/descriptor.upb.c
         2.0%     356    bazel-out/k8-opt/bin/external/com_google_protobuf/conformance/conformance.upb.c
         1.4%     264    bazel-out/k8-opt/bin/external/com_google_protobuf/google/protobuf/struct.upb.c
         0.5%      88    [2 Others]
         0.3%      48    bazel-out/k8-opt/bin/external/com_google_protobuf/google/protobuf/any.upb.c
         0.3%      48    bazel-out/k8-opt/bin/external/com_google_protobuf/google/protobuf/duration.upb.c
      10.0%  12.9Ki    Binary Format
        54.0%  6.95Ki    upb/decode.c
        46.0%  5.92Ki    upb/encode.c
       8.8%  11.3Ki    Core Runtime
        66.3%  7.52Ki    upb/table.c
        17.8%  2.02Ki    upb/upb.c
        15.9%  1.80Ki    upb/msg.c
       5.6%  7.19Ki    [34 Others]
       3.8%  4.94Ki    Text Format
       100.0%  4.94Ki    upb/text_encode.c
       3.5%  4.46Ki    [section .rodata]
     100.0%   128Ki    TOTAL
So JSON is pulling in 25Ki directly, plus it depends on the 19Ki reflection runtime and the generated reflection.

But it is all a la carte: if you don't want JSON, just don't use it and don't link it in.

(btw that 7Ki for upb/table.c is to support protobuf maps, I want to make that a la carte also so that you don't pay for it when you don't use maps).

Re: Don't Use Protobuf for Telemetry

#75
post #64
post #54

Earlier quoted context omitted.

Things may have improved since, but the implementations are somehow very large and slow. Things may have changed since, but AFAIK the C++ implementation would always allocate on the heap for nested messages, and perhaps even for optional scalars. This may be optimal for larger documents, but not for smallish messages (my use case was market data and trading instructions). I measured certain small messages, where an e…

> AFAIK the C++ implementation would always allocate on the heap for nested messages FWIW if you reuse the same message object for multiple parsings, it will re-use the sub-objects as well, thus amortizing away the allocation cost. Parsing the same message into the same object twice should do zero allocations on the second parse. This is the intended way to use Protobuf for small-size messages. Apparently the C++ imp…

But then all that must come with bookkeeping, which brings its own cost.

Take a look at an implementation like Prost, for Rust. It's very similar to what I did (10 years ago by now). Everything is just inline, except when messages can be recursive (which should be rare for most protocols).

Re: Don't Use Protobuf for Telemetry

#76
post #4

His point on protobuf-java library adding nontrivial bloat to his Java app is definitely valid. However, his other argument about protobuf wire format being inefficient is hard to square with decades of practical experience Google had with protobufs, which are used for literally everything, including telemetry, and high throughput, low latency applications. Sure, having to recursively precompute lengths before serial…

It seems easy to square the practical experience of protobufs at Google versus the tradeoffs encountered in the wire format when you take into account how many Google engineers have directly contributed to the diaspora of related formats such as CapnProto, flatbuffers, msgpack, etc with different tradeoffs (especially for smaller scales than Google). It seems clear enough that protobufs were optimized in a scale that…

> It seems clear enough that protobufs were optimized in a scale that involved a lot of time/cost-sensitive reads and far fewer time/cost-sensitive writes.

Nah, you're assuming too much. Protobuf was thrown together in a fairly ad hoc way by a couple (brilliant!) engineers (Jeff and Sanjay) to help make the Google search index protocol easier to maintain. The specific design decisions in Protobuf were not carefully tested or weighed against other possibilities. They just did something that worked well enough, and it worked well enough that it was rapidly adopted by the rest of the company. It was then too late to change anything.

Yes, the fact that a variable-width size must be written before the data is a kind of big problem, which essentially requires you to make two passes over the message tree, one to compute sizes and one to write the data. There are some clever optimizations that can reduce the impact, but I don't think the designers would make the same decision if starting from scratch with no need to support legacy. But there was no point in history where it was worth breaking compatibility to fix this issue, so that's how it remains. It's a problem, just not that big a problem.

(I maintained Protobuf for several years, including writing version 2 and open sourcing it.)

Re: Don't Use Protobuf for Telemetry

#77
post #72
post #31

I have written an in-house implementation of protobuf for C++ (sorry can't share) and studied the wire format extensively. Google's implementations, at least C++ and Java, are a bunch of bloated crap (or maybe they're very good, but for a use case that I haven't yet encountered). Don't shoot down the format because of a specific implementation, find or write a better one and enjoy the fact that every language has at…

If you truly wanted speed, why not Cap'n'Proto?

It didn't exist yet. We were evolving from simple raw messages with all sorts of problems.

The alternative was something custom again, with better support for schema evolution, but pb was convenient due to existing implementations in Python (system tests) and C# (UI).

Re: Don't Use Protobuf for Telemetry

#78
post #54

Earlier quoted context omitted.

Things may have improved since, but the implementations are somehow very large and slow. Things may have changed since, but AFAIK the C++ implementation would always allocate on the heap for nested messages, and perhaps even for optional scalars. This may be optimal for larger documents, but not for smallish messages (my use case was market data and trading instructions). I measured certain small messages, where an e…

Since all other comments appear to contradict you and make apologies from authority (after all, Google can't do anything wrong, right?) I'd like to just reassure you with my 20 years of experience developing C (and the last 10 with C++) in network and system software: that any library -- any library -- that forces internal dynamic memory upon its user smells bad. It's not a universal condemnation, but it begs the que…

> No zero-copy for networking? Forced internal heap allocations with only this arena feature after a decade? Sorry no. Protobufs isn't useful for serious network applications.

That's a bit harsh. Protobufs deliver smaller wire size than any of the newer "zero-copy" formats. And many receivers of zero-copy formats will... copy the data into some internal representation. If your protobuf implementation delivers classes that are good enough to work with internally (store in maps, forward, etc) then you don't really lose something; instead you gain, due to no manual conversion layer.

Re: Don't Use Protobuf for Telemetry

#79
post #59

Earlier quoted context omitted.

CP was written by Kenton Varda, who spent many years working on protobuf at Google. One massive advantage of protobuf for mainstream Google languages (C++, Java) is that Google has used them extremely heavily for many years, and you can trust that they've been extensively battle tested in Google's enormous high-traffic services, and scrutinized by Google's vast army of engineers. Kenton's experience notwithstanding,…

Eh... I'd actually argue that Protobuf's big advantage is all the languages it supports that Cap'n Proto doesn't. If you're using C++ exclusively, I'd argue Cap'n Proto beats Protobuf. That "army of engineers" isn't necessarily the advantage you think it is -- rather than forcing Protobuf to be the best it can be, I would argue they forced Protobuf to get stuck with early design decisions that no one thinks were idea…

[deleted]

Re: Don't Use Protobuf for Telemetry

#80
post #54

Earlier quoted context omitted.

Things may have improved since, but the implementations are somehow very large and slow. Things may have changed since, but AFAIK the C++ implementation would always allocate on the heap for nested messages, and perhaps even for optional scalars. This may be optimal for larger documents, but not for smallish messages (my use case was market data and trading instructions). I measured certain small messages, where an e…

Since all other comments appear to contradict you and make apologies from authority (after all, Google can't do anything wrong, right?) I'd like to just reassure you with my 20 years of experience developing C (and the last 10 with C++) in network and system software: that any library -- any library -- that forces internal dynamic memory upon its user smells bad. It's not a universal condemnation, but it begs the que…

> make apologies from authority (after all, Google can't do anything wrong, right?)

That's not my position at all. In my other comment (https://news.ycombinator.com/item?id=25586447) I explain how I've spent 10 years trying to improve on protobuf C++ precisely because I agree that some of these limitations are unnecessary.

> No zero-copy for networking? Forced internal heap allocations with only this arena feature after a decade? Sorry no. Protobufs isn't useful for serious network applications.

I suppose it depends what you are comparing it to. Almost every JSON library has the same limitations you mentioned, and yet many people find JSON useful for network applications. But I agree that giving users full control over allocations makes a library useful in many more situations.

I think arena allocation is a pretty reasonable solution to the problem. You can use whatever memory you want for the arena (stack, heap, static buffer) and you can constrain it so that no heap allocations are allowed.

Unfortunately protobuf C++ can't live fully within this arena model while it uses std::string for accessors. Hopefully this can be fixed at some point.

Post reply on HN