forked from tomba/netserializer
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathREADME
268 lines (218 loc) · 14.2 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
NetSerializer - A fast, simple serializer for .Net
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.
Supported Types
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 NetSerializer 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
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);
Performance
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) |
== 100 LargeStruct x 30000 ==
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 | |
== 100 LargeStruct x 30000 (direct) ==
NetSerializer | MemStream Serialize | 463 | 0 0 0 | 1759 |
NetSerializer | MemStream Deserialize | 413 | 0 0 0 | |
NetSerializer | NetTest | 661 | 0 0 0 | |
== 100 Guid x 50000 ==
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 | |
== 100 Guid x 50000 (direct) ==
NetSerializer | MemStream Serialize | 642 | 0 0 0 | 1876 |
NetSerializer | MemStream Deserialize | 499 | 0 0 0 | |
NetSerializer | NetTest | 819 | 0 0 0 | |
== 100 Int32 x 100000 ==
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 | |
== 100 Int32 x 100000 (direct) ==
NetSerializer | MemStream Serialize | 468 | 0 0 0 | 361 |
NetSerializer | MemStream Deserialize | 428 | 0 0 0 | |
NetSerializer | NetTest | 580 | 0 0 0 | |
== 100 U8Message x 100000 ==
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 | |
== 100 U8Message x 100000 (direct) ==
NetSerializer | MemStream Serialize | 281 | 0 0 0 | 100 |
NetSerializer | MemStream Deserialize | 1047 | 152 0 0 | |
NetSerializer | NetTest | 1156 | 152 0 0 | |
== 100 S16Message x 100000 ==
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 | |
== 100 S32Message x 100000 ==
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 | |
== 100 S64Message x 100000 ==
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 | |
== 100 DecimalMessage x 50000 ==
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 | |
== 100 NullableDecimalMessage x 100000 ==
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 | |
== 100 PrimitivesMessage x 10000 ==
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 | |
== 10 DictionaryMessage x 1000 ==
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 | |
== 100 ComplexMessage x 10000 ==
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 | |
== 100 StringMessage x 20000 ==
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 | |
== 100 StructMessage x 20000 ==
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 | |
== 100 BoxedPrimitivesMessage x 20000 ==
NetSerializer | MemStream Serialize | 708 | 0 0 0 | 1723 |
NetSerializer | MemStream Deserialize | 570 | 223 0 0 | |
NetSerializer | NetTest | 862 | 223 0 0 | |
== 10000 ByteArrayMessage x 1 ==
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 | |
== 1000 IntArrayMessage x 1 ==
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 | |
== 10 TriDimArrayCustomSerializersMessage x 100 ==
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.