5 ms·
Someone's bound to say "But it doesn't support RPC!" No, it doesn't, and that's a good thing. Use ZeroMQ as your transport with a protobuff payload and do remo
by JoelPM 16y ago
Someone's bound to say "But it doesn't support RPC!"
No, it doesn't, and that's a good thing. Use ZeroMQ as your transport with a protobuff payload and do remote message passing* instead of RPC - it'll scale more efficiently than straight RPC and you won't end up having to pool connections or queue requests in your client.
*Give each message request an identifier and have each response identify what message it's answering.
- alexgartrell 16y agoA research project with which I am involved recently moved from google protocol buffers + TCP to thrift RPC, because they found that thrift was much faster. Google Protocol buffers encode the keys and field types with varints, which are basically series of bytes of arbitrary length to represent ints. If the 8th bit is set, move on to the next byte. It's pretty straightforward int parse(uchar *msg) { int x = 0; do { x <<= 7; x |= *msg; } while(*msg & 0x80); return x; } Thrift, on the other hand, uses vanilla 4-byte ints for the same purpose. This has the effect of limited the number of fields to 2^29 (I think they use the bottom 3 bits to specify field type, but I don't remember off the top of my head). On the flip side, it's pretty instant to parse, as opposed to the looping thing. Further, thrift makes it pretty trivial to write a thrift server/client in any language. In short, thrift > protocol buffers. Edit: One caveat -- Async calls WITH return values are more or less a no-go in thrift.
- nostrademons 16y agoI suspect that this is heavily dependent upon the relative speeds of network vs. CPU, as well as the usage that these encoded messages will get afterwards. Protocol buffers are making a speed vs. space tradeoff, minimizing the size of a message at the cost of some processing power. That probably made a lot more sense when Google was young and 100BaseT was new than in the era with ubiquitous gigabit Ethernet that we've got now. Also remember that for most production services, you'll want to log requests & responses. As soon as you start hitting the disk, increased space usage gets expensive pretty fast. You'd probably want to convert your 32-bit ints to varints then anyway, and then you've lost all the CPU that you saved in the first place. In short, YMMV. Consider your particular use-case carefully before making any blanket judgments of thrift > protobufs or protobufs > thrift.
- alexgartrell 16y agoYou're talking about saving 3 bytes per int. I'd imagine it's pretty hard to find a workload where you can achieve 75% compression by using exclusively varints that are less than 128 in magnitude. In fact, I'd imagine that most of the data shipped in most workloads is mostly strings/binary, which have very little if any savings. So let's talk about the ideal situation where we have exclusively fields that are varints that are limited to 128. This means that each will be 2 bytes (1 for field data + 1 for the varint itself, both of which are under 128 in magnitude). How many of these can we fit? Well, if we use the bottom 3 bits for specifying the type (a conservative estimate) that leaves only 4 bits (remember, subtract 1 for varint or not) to ID them, which means 16. So if a message has 16 fields and we save 6 bytes per request, we're talking a savings of 96 bytes. For the next 2048 entries, we're saving, 5 bytes per (5kb vs 15kb), then beyond that, we're saving 4 bytes per (and eventually none). This is also an incredibly arbitrary situation where you expect ALL of your fields to be small ints. Same with disk, it's unlikely that your workloads will allow you to save a lot by using varints. So, you're right that, for some workloads, this may be better, but if someone put a gun to my head and said "protocol buffers or thrift?" I'd have to go with thrift.
- 124816 16y agoGoing to reply to all your comments at this rate. ;-) Even with single-byte tags, the overhead can be quite significant; thus the packed = true option. Unfortunately I don't have any public protocols or data to show you, but by switching to packed = true (combined with some extra tricks) I achieved ~40-60% savings on some of our datasets. We use protobufs for our stable storage, and this change certainly saved us more than my yearly compensation package. > For the next 2048 entries Yikes! I hope you never have to work with a message type with anywhere near 2048 fields! :-) In practice, more than half message types have less than 16 fields, which is why the "incredibly arbitrary situation" is justified, and really does end up saving a ton of storage. But, for simplicity, nothing can beat just ntohl and a cast.
- dzorz 16y agoYou could've used fixed32 instead of int32 (varint)... Just sayin' :-)
- alexgartrell 16y agoNot for the field keys :) But yes, for the actual data you're in no way bound to varints and it's worth stating so.
- 124816 16y agoHave you run benchmarks on this? If so, can you link me to them? It would be relatively easy to modify protobuf's parsing code to operate on the encoded byte rather than decoding field tags. If it's worth doing I'll do it. BTW, the default parsing code has no loops in the fast-path. I don't remember off the top of my head, but I think both 1 and 2 byte cases are inlined. (Avoiding the loops is great for these cases, since default branch prediction will often be wrong.) All tags under 16 are one byte. Also, in my experience IO has always been the bottleneck -- my CPU can easily keep up with 1 or 10 gig pipes I have access to.
- piotrSikora 16y agoMaybe you should move straight to Apache Avro? From what I've heard, it's a lot more sane than both Protobuf and Thrift.
- JoelPM 16y agoYou raise valid points, so I'll elaborate on my thinking: I don't really care what binary serialization format is used (personally, I use thrift), there's no shortage of good ones available (msgpack, thrift, protobuffs, avro, etc). I think it's more important to decouple the serialization format from the transport and protocol, so that when/if you realize you need to switch you only have to replace one piece.
- deleted 16y ago[deleted]
- _sh 16y agoHuh? I use protocol buffers for rpc all the time. It's even supported in the .proto message format, to wit: rpc Search(SearchTermProto) returns (SearchResultProto); Have I missed something?
- 124816 16y agoProtobufs allow you to define your service, but no RPC system is included.