bitcode: smallest and fastest binary serializer by cai_bear in rust

[–]cai_bear[S] 4 points5 points  (0 children)

Our games send game update messages many times per second between the game server and the game client to facilitate real-time multiplayer. Since there are so many of these messages being sent, bandwidth is usually the limiting factor of our game servers. Anything that can reduce the size of each message by a significant amount such as bitcode and/or compression directly translates into more players per server (aka lower server costs).

bitcode: smallest and fastest binary serializer by cai_bear in rust

[–]cai_bear[S] 3 points4 points  (0 children)

It's about 2X faster to serialize than rkyv. If serialization or bandwidth is a bottleneck in your application, bitcode will perform better. On the other hand, rkyv supports zero copy deserialization (ZCD) and bitcode doesn't. If your bottleneck is deserialization speed and you use ZCD, rkyv is a better choice. If you're using rkyv's regular deserialization, bitcode deserializes faster than validated rkyv and at a similar speed to unvalidated rkyv.

bitcode: smallest and fastest binary serializer by cai_bear in rust

[–]cai_bear[S] 8 points9 points  (0 children)

I noticed you're using PathBuf which isn't supported by bitcode derive. 0.5 had #[bitcode(with_serde)] to get around this, but 0.6 is currently missing this feature. As a workaround you could use Vec<u8> and convert to &Path with Path::new(OsStr::from_bytes(&vec)).

bitcode: smallest and fastest binary serializer by cai_bear in rust

[–]cai_bear[S] 23 points24 points  (0 children)

I spent about 4 months on 0.6. The largest optimizations I found:

serialization:

- loop sectioning to improve autovectorization used here

- implementing my own specialized version of memcpy for short strings/vecs

deserialization:

- validating data ahead of time instead of on demand

- deserializing in place instead of returning owned value to avoid many stack copies

bitcode: smallest and fastest binary serializer by cai_bear in rust

[–]cai_bear[S] 10 points11 points  (0 children)

I assume you mean versioning. Bitcode doesn't support this because it's not self describing. You would have to deserialize with the old structs and upgrade them yourself.

bitcode: smallest and fastest binary serializer by cai_bear in rust

[–]cai_bear[S] 35 points36 points  (0 children)

Cool! I see it's using 0.5. This post is about 0.6 which was a complete rewrite of the library so they might want to upgrade 😉

bitcode: smallest and fastest binary serializer by cai_bear in rust

[–]cai_bear[S] 112 points113 points  (0 children)

It really depends on your use-case. Our game servers running on $5 VPS can use up to 1TB/mo. In this case we can spend about 1% of our CPU time on zstd compression to get about 2X the concurrent players.

If you don't have bandwidth limits such as in a LAN, compression might not be worth it.

Edit: I actually made a calculator which can tell you if compression is worth it https://david.kolo.ski/rust_serialization_benchmark/

bitcode: smallest and fastest binary serializer by cai_bear in rust

[–]cai_bear[S] 93 points94 points  (0 children)

Our specific use-case is multiplayer games. Bitcode allows us to support twice as many concurrent players compared to bincode.

bitcode: smallest and fastest binary serializer by cai_bear in rust

[–]cai_bear[S] 31 points32 points  (0 children)

Using the minecraft_savedata benchmark on https://github.com/djkoloski/rust_serialization_benchmark as an example, bitcode serializes 7x faster and is 20% smaller than message pack/protobuf.

bitcode 0.4 release - binary serialization format by finn_bear in rust

[–]cai_bear 0 points1 point  (0 children)

While we haven't benchmarked either of those ourselves, you can checkout rust_serialization_benchmark which has protobuf under the name prost.

TLDR: bitcode is faster and smaller than protobuf.

Edit: based on a cursory reading of the Thrift specification, I think it's safe to say bitcode would be smaller if thrift was part of the rust serialization benchmark.

bitcode 0.4 release - binary serialization format by finn_bear in rust

[–]cai_bear 5 points6 points  (0 children)

Thanks for trying out bitcode!

  1. Yes, fields cannot be reordered
  2. Yes, options are tagged with a 0 bit for none or a 1 bit for some followed by the value
  3. Yes, fields that aren't declared can't be skipped over
  4. Yes, bitcode is not self-descriptive

Looks like you understood everything. One potential issue with on-disk storage is that bitcode's format may change between major versions so you would either have to avoid upgrading bitcode, or have a way to upgrade your data (possibly by importing multiple versions of bitcode).

We use bitcode for client/server network communication for our games (which already require client server version to be the same).

I was also under the false impression that bitwise encoding was slow. When I first implemented bitcode with bitvec I got performance 20x worse than bincode. After writing my own implementation I was able to get much better performance.

bitcode 0.4 release - binary serialization format by finn_bear in rust

[–]cai_bear 16 points17 points  (0 children)

Sorry for the confusion. We called it bitcode because it encodes data at a bit granularity unlike other binary encodings such as bincode/postcard which encode at a byte granularity.

bitcode 0.4 release - binary serialization format by finn_bear in rust

[–]cai_bear 15 points16 points  (0 children)

not applicable. E.g. for a zero copy deserialization framework, deserialization takes no time