My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
Performance  
Is it quick?
Updated Feb 4, 2010 by marc.gravell

Performance

Test Conditions

The test rig is in the examples project. Timings (ms) are measured with Stopwatch; serializations are to Stream.Null; deserialization are from a MemoryStream; each cycle follows a preliminary seriliazation (for JIT) and a forced GC.Collect. Size is bytes.

Encoding Document

A first set of numbers, based on the samples in the encoding document, running in release mode at the console, with the MS .NET 3.5 (SP1 beta) runtime.

(r145; 1,000,000 iterations)

Test1

Serializersizeserializedeserialize
protobuf-net32681,881
proto#3761,792
BinaryFormatter1536,6948,420
SoapFormatter68728,60955,125
XmlSerializer15314,59419,819
DataContractSerializer2053,26310,516
DataContractJsonSerializer262,85415,621

Test2

Serializersizeserializedeserialize
protobuf-net93562,178
proto#92552,139
BinaryFormatter1616,4388,622
SoapFormatter70228,16456,211
XmlSerializer15714,03718,572
DataContractSerializer1512,8769,387
DataContractJsonSerializer151,91911,335

Test3

Serializersizeserializedeserialize
protobuf-net58423,535
proto#53453,497
BinaryFormatter25111,61815,178
SoapFormatter92847,17979,218
XmlSerializer17015,37322,216
DataContractSerializer2123,77811,455
DataContractJsonSerializer324,12918,728

Conclusion

For small fragments, proto is an order of magnitude quicker, but all variants are still extremely fast (on the µs scale). Note that the short length of the type/field names positively skews the size for the MS serializers (i.e. makes them shorter). In general the difference is even larger; protocol buffers don't use names, so aren't impacted.

Northwind

A more realistic performance metric is obtained using a Northwind data-extract via LINQ-to-SQL; in this case we can't present binary formatter numbers (BinaryFormatter / SoapFormatter) because some of the classes involved (from LINQ-to-SQL) do not support such usage.

This metric based on the data from nwind.proto.bin, which has nearly 3000 objects (orders and order-lines).

(r145; 1000 iterations; proto# is r75)

Serializersizeserializedeserialize
protobuf-net (1)133,0105,77913,224
protobuf-net (2)132,2634,65714,045
proto# (3)130,1114,59814,939
DataContractSerializer736,57415,10071,688
DataContractJsonSerializer490,42529,399136,421

1 = using length-prefixed sub-messages

2 = using grouped sub-messages

3 = using packed encoding for DateTime, Decimal etc

Conclusion

needs update (post r122): have improved performance for length-prefix deserialization.

The first observation has to be that using groups (rather than length-prefixed) sub-messages provides a noticeable improvement in performance; this is because the length must be written first, and calculating this (which includes all descendents) is almost as expensive as performing the serialization. The system deliberately avoids the need to buffer the entire sub-message binary, since this could be very large and would involve nested buffers for each level, bloating memory usage. Likewise, deserializing a nested message currently involves indirection via a length-limited sub-stream (although this could perhaps be improved).

In contrast; to writing groups is simple: write a starter; write the data; write a terminator - the cost of calculating the length is eliminated.

Both variants provide a performance improvement over DataContractSerializer, with deserialization being most noticeable.

Additional

As part of the "remoting" unit tests, I added a clone of the Northwind sample, but without using the LINQ classes. Fortunately Serializer.ChangeType() makes it easy to fill this clone from the existing objects! This allows us to benchmark xml/binary serializer performance for the large payloads. For BinaryFormatter / XmlSerializer, I have also implemented ISerializable (via a byte[]) / IXmlSerializable (via base-64) using protobuf-net for comparison. Note that with the known issues with generics, SoapFormatter simply isn't worth the pain of testing for complex data...

Simple compatible classes (using length-prefix for sub-messages):

Serializersizeserializedeserialize
protobuf-net (1)133,0106,00112,140
protobuf-net (2)132,2634,75011,933
proto# (3)130,1114,84413,879
BinaryFormatter276,30269,37158,617
XmlSerializer1,043,13729,46538,770
DataContractSerializer772,40615,15967,924
DataContractJsonSerializer490,42528,338135,935

Advanced (ISerializable / IXmlSerializable) compatible classes (using length-prefix for sub-messages):

Serializersizeserializedeserialize
protobuf-net133,0106,16512,538
BinaryFormatter133,1557,50412,284
XmlSerializer177,4107,59919,668

This shows that protocol buffers can be used to offer reduced size and increased performance, even with the standard serializers. In particular, note that when coupled with protobuf-net, XmlSerializer only needs 1/6th of the space with comparable time, and BinaryFormatter halves both space and time.

Remoting

The test project includes remoting examples (SmallPayload() and LargePayload()), designed to evaluate the performance of using protobuf-net to implement ISerializable, rather than using the native formatter. I don't have exact numbers at the moment, but for local tests (i.e. 2 AppDomains in the same process) the performance depends on the payload:

  • For large payloads (I have used the Northwind example, above), protobuf-net halves round-trip time, presumably by improving on the BinaryFormatter speed
  • For small payloads, protobuf-net makes things slower

It is expected that for remote tests the bandwidth will be the biggest factor; as such protocol-buffer usage should show a considerable improvement; figures to follow...

Comment by muratoz...@gmail.com, Jun 25, 2009

woow, great job! thank you.

Comment by jri...@gmail.com, Jul 2, 2009

Thanks for the excellent comparison, Marc. Your protobuf-net implementation of protocol buffers (as well as protocol buffers themselves) definitely seems like a very efficient alternative to web services, and it will be a prime option for all my future SOA initiatives.

Comment by jeffrey....@gmail.com, Dec 6, 2010

If possible I think it would be useful to include the NetDataContractSerializer? along with the other serializes in the comparisons.

Comment by j...@plane9.com, Jun 11, 2011

Nice work! It would be interesting to compare this with http://www.codeproject.com/KB/IP/fastJSON.aspx and/or Json.Net. Since they seem a lot faster than DataContractJsonSerializer?. Might even come quite close to propbufers performance.

Comment by kwang1i...@gmail.com, Dec 31, 2011

Is this 268 ms for 1,000,000 iterations? If so that is unbelievable.


Sign in to add a comment
Powered by Google Project Hosting