NetSerializer is a simple and very fast serializer for .Net languages. It is the fastest serializer I have found for my use cases.
The main pros of NetSerializer are:
- Excellent for network serialization
- Supports classes, structs, enums, interfaces, abstract classes
- No versioning or other extra information is serialized, only pure data
- No type IDs for primitive types, structs or sealed classes, so less data to be sent
- No dynamic type lookup for primitive types, structs or sealed classes, so deserialization is faster
- No extra attributes needed (like DataContract/Member), just add the standard [Serializable]
- Thread safe without locks
- The data is written to the stream and read from the stream directly, without the need for temporary buffers or large buffers
The simpleness of NetSerializer has a drawback which must be considered by the user: no versioning or other meta information is sent, which means that the sender and the receiver have to have the same versions of the types being serialized. This means that it's a bad idea to save the serialized data for longer periods of time, as a version upgrade could make the data non-deserializable. For this reason I think the best (and perhaps only) use for NetSerializer is for sending data over network, between a client and a server which have verified version compatibility when the connection is made.
Bitcoin: 13YgwAye9Uz85xzjZXV4Son9uA6Kwy1ZaA
NetSerializer supports serializing the following types:
- All primitive types (Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single)
- Strings
- Enums
- Single dimensional arrays
- Structs and classes marked as [Serializable]. Note that NeteSrializer does not support ISerializable, so not all classes marked with [Serializable] are supported. However, NetSerializer has special support for the following ISerializable classes:
- Dictionary<K,V>
- DateTime
NetSerializer also supports creating custom serializers. Custom serializers can be used to serialize types not directly supported by NetSerializer.
Usage is simple. The types to be serialized need to be marked with the standard
[Serializable]
. You can also use [NonSerialized]
for fields you don't want to
serialize. Nothing else needs to be done for the types to be serialized.
Then you need to initialize NetSerializer by giving it a list of types you will be serializing. NetSerializer will scan through the given types, and recursively add all the types used by the given types, and create (de)serialization code.
Initialization:
var ser = new Serializer(types);
Serializing:
ser.Serialize(stream, ob);
Deserializing:
(YourType)ser.Deserialize(stream);
Below is a performance comparison between NetSerializer and protobuf-net. Protobuf-net is a fast Protocol Buffers compatible serializer, which was the best serializer I could find out there when I considered the serializer for my use case.
The tests create an array of N items of particular type, created with random data. The items are then serialized, and this is repeated M times for the same dataset. NetSerializer can also serialize types directly, without writing any meta information or boxing of value types. These tests are marked with "(direct)".
The table lists the time it takes run the test, the number of GC collections (per generation) that happened during the test, and the size of the outputted serialized data (when available).
There are three tests:
-
MemStream Serialize - serializes an array of objects to a memory stream.
-
MemStream Deserialize - deserializes the stream created with MemStream Serialize test.
-
NetTest - uses two threads, of which the first one serializes objects and sends them over a local socket, and the second one receive the data and deserialize the objects. Note that the size is not available for NetTest, as tracking the sent data is not trivial. However, the dataset is the same as with MemStream, an so is the size of the data.
The details of the tests can be found from the source code. The tests were run on a 64bit Windows 10 laptop.
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 526 | 91 0 0 | 1859 |
NetSerializer | MemStream Deserialize | 439 | 91 0 0 | |
NetSerializer | NetTest | 751 | 184 0 0 | |
protobuf-net | MemStream Serialize | 987 | 381 0 0 | 2151 |
protobuf-net | MemStream Deserialize | 1586 | 183 0 0 | |
protobuf-net | NetTest | 2151 | 566 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 463 | 0 0 0 | 1759 |
NetSerializer | MemStream Deserialize | 413 | 0 0 0 | |
NetSerializer | NetTest | 661 | 0 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 733 | 101 0 0 | 1964 |
NetSerializer | MemStream Deserialize | 509 | 101 0 0 | |
NetSerializer | NetTest | 939 | 204 0 0 | |
protobuf-net | MemStream Serialize | 4487 | 890 0 0 | 2100 |
protobuf-net | MemStream Deserialize | 4503 | 279 0 0 | |
protobuf-net | NetTest | 5803 | 1178 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 642 | 0 0 0 | 1876 |
NetSerializer | MemStream Deserialize | 499 | 0 0 0 | |
NetSerializer | NetTest | 819 | 0 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 617 | 152 0 0 | 461 |
NetSerializer | MemStream Deserialize | 426 | 152 0 0 | |
NetSerializer | NetTest | 742 | 306 0 0 | |
protobuf-net | MemStream Serialize | 8778 | 1527 0 0 | 648 |
protobuf-net | MemStream Deserialize | 9078 | 560 14 0 | |
protobuf-net | NetTest | 11416 | 2103 1 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 468 | 0 0 0 | 361 |
NetSerializer | MemStream Deserialize | 428 | 0 0 0 | |
NetSerializer | NetTest | 580 | 0 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 389 | 0 0 0 | 200 |
NetSerializer | MemStream Deserialize | 991 | 152 0 0 | |
NetSerializer | NetTest | 1137 | 152 0 0 | |
protobuf-net | MemStream Serialize | 3007 | 966 0 0 | 527 |
protobuf-net | MemStream Deserialize | 5745 | 152 0 0 | |
protobuf-net | NetTest | 6925 | 1122 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 281 | 0 0 0 | 100 |
NetSerializer | MemStream Deserialize | 1047 | 152 0 0 | |
NetSerializer | NetTest | 1156 | 152 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 512 | 0 0 0 | 341 |
NetSerializer | MemStream Deserialize | 1110 | 152 0 0 | |
NetSerializer | NetTest | 1347 | 152 0 0 | |
protobuf-net | MemStream Serialize | 3204 | 966 0 0 | 844 |
protobuf-net | MemStream Deserialize | 5986 | 152 0 0 | |
protobuf-net | NetTest | 7325 | 1122 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 593 | 0 0 0 | 461 |
NetSerializer | MemStream Deserialize | 1207 | 152 0 0 | |
NetSerializer | NetTest | 1475 | 152 0 0 | |
protobuf-net | MemStream Serialize | 3371 | 966 0 0 | 828 |
protobuf-net | MemStream Deserialize | 6066 | 152 0 0 | |
protobuf-net | NetTest | 7535 | 1120 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 699 | 0 0 0 | 542 |
NetSerializer | MemStream Deserialize | 1313 | 152 0 0 | |
NetSerializer | NetTest | 1603 | 152 0 0 | |
protobuf-net | MemStream Serialize | 3512 | 966 0 0 | 809 |
protobuf-net | MemStream Deserialize | 6268 | 152 0 0 | |
protobuf-net | NetTest | 7672 | 1124 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 648 | 127 0 0 | 1360 |
NetSerializer | MemStream Deserialize | 934 | 101 0 0 | |
NetSerializer | NetTest | 1292 | 229 0 0 | |
protobuf-net | MemStream Serialize | 2716 | 610 0 0 | 2022 |
protobuf-net | MemStream Deserialize | 4220 | 101 0 0 | |
protobuf-net | NetTest | 5052 | 713 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 441 | 7 0 0 | 238 |
NetSerializer | MemStream Deserialize | 1156 | 254 0 0 | |
NetSerializer | NetTest | 1340 | 262 0 0 | |
protobuf-net | MemStream Serialize | 4033 | 973 0 0 | 353 |
protobuf-net | MemStream Deserialize | 6727 | 254 0 0 | |
protobuf-net | NetTest | 8203 | 1233 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 703 | 25 0 0 | 5286 |
NetSerializer | MemStream Deserialize | 748 | 76 0 0 | |
NetSerializer | NetTest | 1010 | 102 0 0 | |
protobuf-net | MemStream Serialize | 725 | 96 0 0 | 7290 |
protobuf-net | MemStream Deserialize | 1089 | 50 0 0 | |
protobuf-net | NetTest | 1348 | 148 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 1310 | 75 0 0 | 86187 |
NetSerializer | MemStream Deserialize | 1955 | 109 54 0 | |
NetSerializer | NetTest | 2521 | 135 67 0 | |
protobuf-net | MemStream Serialize | 1713 | 413 0 0 | 142035 |
protobuf-net | MemStream Deserialize | 3576 | 233 116 0 | |
protobuf-net | NetTest | 4693 | 494 247 11 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 410 | 0 0 0 | 2838 |
NetSerializer | MemStream Deserialize | 608 | 100 0 0 | |
NetSerializer | NetTest | 812 | 100 0 0 | |
protobuf-net | MemStream Serialize | 838 | 96 0 0 | 5087 |
protobuf-net | MemStream Deserialize | 1902 | 100 0 0 | |
protobuf-net | NetTest | 2195 | 198 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 447 | 0 0 0 | 4886 |
NetSerializer | MemStream Deserialize | 655 | 182 0 0 | |
NetSerializer | NetTest | 903 | 182 0 0 | |
protobuf-net | MemStream Serialize | 1043 | 193 0 0 | 5085 |
protobuf-net | MemStream Deserialize | 3973 | 182 0 0 | |
protobuf-net | NetTest | 2428 | 378 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 528 | 0 0 0 | 2455 |
NetSerializer | MemStream Deserialize | 681 | 117 0 0 | |
NetSerializer | NetTest | 909 | 117 0 0 | |
protobuf-net | MemStream Serialize | 1369 | 274 0 0 | 3622 |
protobuf-net | MemStream Deserialize | 4297 | 280 0 0 | |
protobuf-net | NetTest | 2752 | 557 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 708 | 0 0 0 | 1723 |
NetSerializer | MemStream Deserialize | 570 | 223 0 0 | |
NetSerializer | NetTest | 862 | 223 0 0 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 736 | 1 1 1 | 498085311 |
NetSerializer | MemStream Deserialize | 325 | 58 29 1 | |
NetSerializer | NetTest | 889 | 58 27 0 | |
protobuf-net | MemStream Serialize | 1329 | 341 6 3 | 498151945 |
protobuf-net | MemStream Deserialize | 418 | 58 29 1 | |
protobuf-net | NetTest | 1696 | 172 40 2 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 1533 | 0 0 0 | 177278871 |
NetSerializer | MemStream Deserialize | 1147 | 2 1 0 | |
NetSerializer | NetTest | 1821 | 3 1 0 | |
protobuf-net | MemStream Serialize | 1849 | 79 4 2 | 283510795 |
protobuf-net | MemStream Deserialize | 1720 | 28 3 0 | |
protobuf-net | NetTest | 2620 | 88 5 2 |
time (ms) | GC coll. | size (B) | ||
---|---|---|---|---|
NetSerializer | MemStream Serialize | 1246 | 0 0 0 | 1601277 |
NetSerializer | MemStream Deserialize | 1175 | 30 27 25 | |
NetSerializer | NetTest | 1844 | 43 41 38 |
As can be seen from the tests, NetSerializer is clearly faster and has smaller memory footprint in about all of the cases. For example, the tests with ComplexMessages show NetSerializer's MemStream Serialize cause zero garbage collections, even though more than 20MB of data is being serialized.