Ask HN: What happened to flatbuffers? Are they being used?
A few years ago, their was a talk about flatbuffers[0] being a memory efficient and quicker method than JSON.
Anyone have any real world experience with it?
Anyone have any real world experience with it?
81 comments
[ 3.5 ms ] story [ 137 ms ] thread2. Is it just putting strings into a big array?
3. Do you have an examples of them being used?
2. No, writers can directly embed structs and primitive data types into binary buffers at runtime through an API generated from an IDL file. Readers use direct memory access to pull values out of the buffers. If you set it up right, this can result in a massive perf boost by eliminating the encoding and decoding steps.
3. Facebook uses them in their mobile app. Another commenter mentioned use of the in the Arrow format. The flatbuffers website isn't the best, but clearly documents the flatbuffers IDL.
They aren't hard to use, but they aren't the easiest thing either.
Once you get the gist of the API through the tutorial, the other important topics that come up immediately are version control; git repo design; headers and framing.
In production, they've been bulletproof. As long as you account for the compile time issues (schema versioning, repo, headers and framing, etc.).
Just too many drawbacks.
For server to server, they might be fine, but to client just stick with JSON. (which when compressed is pretty efficient).
Also browser has JSON parsing built in. Less dependencies. Easier tooling overall.
In my experience people overuse protobuf. But I also worked at Google, where it's the hammer in constant search of any nail it can find.
At the very least, endpoints should provide the option to provide a JSON form through content representation negotiation.
We moved to flatbuffers and back to JSON because in the end of the day, for our data, data compression with JSON+gzip was similarly-sized than the original one (which had some other fields that we were not using) and 10-20 times faster to decode.
That said, the use case for flatbuffers and capnproto isn't really about data size, it's about avoiding unnecessary copies in the processing pipeline. "Zero copy" really does pay dividends where performance is a concern if you write your code the right way.
Most people working on typical "web stack" type applications won't hit these concerns. But there are classes of applications where what flatbuffers (and other zerocopy payload formats) offer is important.
The difference in computation time between operating on something sitting in L1 cache vs not-in-cache is orders of magnitude. And memory bandwidth is a bottleneck in some applications and on some machines (particularly embedded.)
I did this one for reading json on the fast path, the sending system laid out the arrays in a periodic pattern in memory that enabled parseless retrieval of individual values.
https://github.com/simdjson/simdjson
Also I assume you'd have to have some sort of binary portion bundled with it to hold the field offsets, no?
I think I'd take a different approach and send along an "offset map" index blob which maps (statically known in advance based on a schema that both client and server would need to agree to) field IDs to memory offsets&lengths into a standard JSON file.
Then you have a readable JSON, but also a fast way O(1) to access the fields in a zero-copy environment.
Done right the blob could even fit in an HTTP response header, so standard clients could use the msg as is while 'smart' clients could use the index map for optimized access.
But as I said would suffer from numeric values being text encoded. And actually 'compiling' the blob would be an extra step. Wouldn't have all the benefits of flatbuffers or capnproto, but could be an interesting compromise.
I'd be surprised if this isn't already being done somewhere.
If yes, is it a less hare-brained idea than using the ctypes Python module to mmap a file as a C struct? That's what I'm currently doing to get 10x speedup relative to SQLite for an application bottlenecked on disk bandwidth, but it's unergonomic to say the least.
Flatbuffers look like a way to get the same performance with better ergonomics, but maybe there's a catch. (E.g. I thought the same thing about Apache Arrow before, but then I realized it's basically read-only. I don't expect to need to resize my tables often, but I do need to be able to twiddle individual values inside the file.)
Tell me more! Is your data larger than memory? You need persistence?
You might take a look at Aerospike, even on a single node if you need low latency persistence.
Regarding files-as-C-structs: That isn't (necessarily) harebrained (if you can trust the input), Microsoft Word .DOC files were just memory dumps. However, twiddling bits inside a flatbuffer isn't recommended per the documentation; rather, the guidance is to replace the entire buffer. If you don't want to manage the IO yourself, then a key/value store that maps indices to flatbuffers is entirely possible. I'd suggest a look at Redis.
That said, the ergonomics are absolutely awful for modifying existing objects; you can't modify an existing object, you need to serialize a whole new object.
There's also a schemaless version (flexbuffers) which retains a number of the flatbuffers benefits (zero-copy access to data, compact binary representation), but is also a lot easier to use for ad-hoc serialization and deserialization; you can `loads`/`dumps` the flexbuffer objects, for example.
grpc and Thrift are mostly backend service interconnects in lieu of RESTful.
Capnproto is also awesome.
https://en.wikipedia.org/wiki/FlatBuffers
Games, data visualization, ... numerically heavy applications mainly.
On a side-note; JSON has been somewhat of a curse. The developer ergonomics of it are so good, that web devs completely disregard how they should layout their data. You know, sending a table as a bunch of nested arrays, that sort of thing. Yuck.
In web apps, data is essentially unusable until it has been unmarshalled. Fine for small things, horrible for data-heavy apps, which really so many apps are now.
Sometimes I wonder if it will change. I'm optimistic that the popularity of mem-efficient formats like this will establish a new base paradigm of data transfer, and be adopted broadly on the web.
How should data be laid out?
wrt numerical data
See if domain allows for more constraint typing (e.g. ints as actual ints). Will push devs naturally to binary formats.
Consider column orientation layout. This will also help with more advanced compression (e.g. delta encoding).
When sticking with JSON, avoid large nested hierarchies that would spam the heap when unmarchalling (ie. prefer [1,2,4,5] over [[1,2],[4,5]].
In general, for large payloads, see if you can avoid deserialization of the payload altogether, and just scan through it. Often times the program just ends up copying values from one place (the file) to another (buffer; DOM-objects), so there's really no need to create to allocate the entire data in heap as many individual objects. That's a hit the user will always feel twice: once at parse (freezing), once at garbage-collection (framerate hickups). You could technically do that with stream parsing of JSON, but then you need special libraries anyway. And once moving to stream-based parsing, you may as well choose a format which has a lot of other advantages as well (e.g. rich type system, column-layouts).
wrt text
It matters less (?). Nonetheless, scannable formats are good here too (e.g. the whole reason we e.g. have line delimited json, to bypass JSONs main limitation).
These are just some general workable ideas. ymmv, ianal, etc..
If your main concern is "faster than JSON" then you're better off using Protocol Buffers simply because they're way more popular and better supported. FlatBuffers are cool because they let you decode on demand. Say you have an array of 10,000 complex objects. With JSON or Protocol Buffers you're going to need to decode and load into memory all 10,000 before you're able to access the one you want. But with FlatBuffers you can decode item X without touching 99% of the rest of the data. Quicker and much more memory efficient.
But it's not simple to implement. You have to write a schema then turn that schema into source files in your target language. There's an impressive array of target languages but it's a custom executable and that adds complexity to any build. Then the generated API is difficult to use (in JS at least) because of course an array isn't a JavaScript array, it's an object with decoder helpers.
It's also quite easy to trip yourself up in terms of performance by decoding the same data over and over again rather than re-using the first decode like you would with JSON or PB. So you have to think about which decoded items to store in memory, where, for how long, etc... I kind of think of it as the data equivalent of a programming language with manual memory management. Definitely has a place. But the majority of projects are going to be fine with automatic memory management.
Worth noting that all these things are true for protobuf as well.
Edit: I was able to find these at https://github.com/hnakamur/protobuf-deb/blob/master/docs/pe... but these numbers don't seem conclusive. Protobuf decode throughput for most schemas tested is much slower than JSON, but protobufs will probably also be a bit smaller. One would have to compare decode throughput for the same documents serialized both ways rather than just looking at a table.
The particular example of a trivial message that is mostly-binary just sounds like a useful test case, more than anything else.
Large values are by no means the only footguns in JSON. Another unfortunately-common gotcha is attempting to encode an int64 from a database (often an ID field) into a JSON number rather than a JSON string, since a JS number type can lead to silent loss of precision.
A more thoughtful serialization format like proto3 binary encoding would avoid both the memory spike issue and the silent loss of numeric precision issue, with the tradeoff that the raw encoded value is not human readable.
It does not support std::string_view (or any equivalent). Google's internal protobuf does support ABSL's string view though, but it's not public.
(Although now std::string_view is common, I hear rumors that the proto API might change…)
https://github.com/kcchu/buffer-benchmarks
[0] https://amazon-ion.github.io
Is the capn'n proto use case similar to something like ZeroMQ or NNG? I'm still not fully sure.
The problem, of course, is that technically worse solutions win for various reasons. One of those reasons is language support. Even if Protocol Buffers is worse in many metrics, it has a lot of language bindings and tools available.
(I work for Google but don't speak for it.)
Basically, we use a rather bandwith-constrained link between our services running in the cloud, and Particle-based IoT devices deployed in many locations. Some locations are remote, some are urban.
I personally haven't had to touch the Flatbuffers code since I joined the company two years ago. It's written and hasn't needed to be maintained.
Seems like a neat idea, but as another commenter said, the usecases where it's the best choice seem pretty narrow.
1) SQLite with BLOB storage gives you binary benefits for file layout and database solutions to metadata, versioning, & indexing into large structure.
2) FlexBuffers look like a more flexible solution within the FlatBuffers library.
https://google.github.io/flatbuffers/flexbuffers.htmlhttps://stackoverflow.com/a/47799699/1020467
3) Might see previous discussion of serialization formats on hn:
https://hn.algolia.com/?dateRange=all&page=0&prefix=false&qu...
[1] https://flatgeobuf.org/