--
Jeremy Leader
jle...@oversee.net
Is there a discussion anywhere of *why* groups are deprecated in favor
of embedded messages?
*dons helmet of robusness and wielding my hammer of parallel thinking*
Guessing wildly here but some reasons for prefering String encoding and hence length prefixed messages.
Robustness, knowing the length beforehand makes it possible to simply drop to large sub-packets and detect malicous or malformed streams. Also knowing the length beforehand gives some opportunities to tweak the submessage allocator or memory pool usage. Furthermore having a bound submessage length could in theory enable parallel submessage parsing making the parser more scalable since each submessage could potentially be parsed in parallel, using group notation makes this prohibitive since finding the end of the submessage bascily as much work as parsing the complete lot. Filtering is also easier (or let's say faster) if the submessage length is know before hand since it enables us to simply skip un(wanted|needed) parts without ever hitting the parser logic.
But yes, begin/end group makes streaming and "firehose" scenarios much more convinient.
My impression is that groups are supported by any implementation used inside google and by "all" (read proto# and protobuf-net) alternative implementations in this thread, basicly I think the worry here is the seriousness of "deprecated" since support is fairly trivial to add and maybe just putting the group/submessage choice into the hands of the user with a submessage default is a pragmatic way to handle this.
The results are probably the by product of using diffrent approaches to building the message to be serialized. Marc's and my implementation both take any class lyinga around annotated with the proper attributes and serializes it to a byte stream. From what I've heard the google implementations uses builder classes or generated classes that computes the size of the resulting message as a by product of initialization/building them, basicly amortizing the cost of length calculation at build time. Currently proto# trades memory efficency for implementation ease by writing nested messages into new buffers and then copying them to their final destination and I belive protobuf-net does on the fly length calculation, both options benefit from group encoding.
Just a small followup on this. I implemented group encoding for submessages and the numbers for serialization compared to my naive length prefix approach is a saving of 40%.
Im guessing it's partly due to my stupid handling of nested messages:
* Write submessage to MemoryStream
* Write length to outer stream
* Copy submessage stream to outer stream.
@Marc: Did you get comparable numbers, how does precalculating submessage length change things?
This is perfect since it gives me a target for the length calculation and as you say that should bring benefits when it comes to memory allocation that I suspect will offset the time needed.
Thanks.
My current default submessage writer creates a new memory stream for each submessage writes to that then copies the content to the target stream. Reusing the same scratchbuffer shaves about 10% off that but has the potential to keep a lingering buffer in memory long after it's usefullness, using group encoding removes any need to move data around and hence lets me write directly to the target stream taking away 40% of the original time. From Kenton's reply indicating that length calculation can be done in around 10% of the total time for serialization Im guessing that length prefix can be as fast, or even faster since it gives the option to preallocate a big enough buffer enabling some potential optimizations when it comes to buffering. Need to investigate this further.
Yes of course as always milage may vary :)
Probably the only way to find out for sure is to implement it and that really wasn't worthwhile under the assumption that it would consume about as much time as actually doing the serialization which is true if you disregard memory movement costs. For me what the group encoding experiment showed was basicly that somewhere close to 40% of my serialization time is spent moving submessages between buffers (using the Northwind proto to have a common ground), in light of that Length calculation really doesn't need to be super speedy to start paying for itself in many scenarios. Also the added benefit of removing some memory pressure from the GC and allocator is probably a good thing globaly in many cases.