DEV Community


Posted on

[P2] Writing a serialization library in C#: Performance is a feature

In my first blog post in the series, I talked briefly about Ion and why it offers a better serialization alternative to popular formats like JSON and XML. In the next few posts, I'll discuss some of the performance features that IonDotnet is implementing.

The reason why I say performance 'feature' is because developers often don't consider it as one. And sometimes for good reason: shipping the product in a timely manner is (and should be) always concerned with the most important thing. Afterall, people say that the first rule of optimization is don't do it. However when it comes to writing a library, especially one that deals with data, I believe performance should be a feature. If you're using my library and your software runs slow, I want it to be your fault, not mine 🤫.

Generally, optimization goes from architecture -> algorithm -> caching -> micro-optimization. In this case, the algorithm is pretty straight forward: Most serializer operates a a state-machine, writing data and updating its states depending on the input. With that being said, even if you don't have to come up with some novel brilliant algorithm, paying attention to some details in the code that, while having nothing to do with the algorithm, will benefit you a big deal.

We'll finish this blog with a simple tip:

If you have a C# struct, ALWAYS override Equals(), == , != and GetHashCode().

One of the great features of C# is of course the ability of define struct Value types. This has a caveat, however: comparing 2 struct by default will force the runtime to use reflections and compare all the nested fields within the structs.

Given how slow reflection is, if you have a struct that you intend to compare a lot, overriding Equals(), == , != and GetHashCode() will give you a huge performance benefit.

In IonDotnet codebase there's a structure called SymbolToken that gets compared with each other a lot. I was careful enough to follow the guidelines and override all the necessary methods (as well as implement IEquatable). I was curious to see how the performance would be if I had missed that. I'm just gonna leave the benchmark result here:

// benchmark, serializing 1000 records
          Method |     Mean |     Error |    StdDev |    Gen 0 |   Gen 1 | Allocated |
--------------------- |---------:|----------:|----------:|---------:|--------:|----------:|
 No-override     | 8.385 ms | 0.1034 ms | 0.0916 ms | 828.1250 | 93.7500 |   3.67 MB |
 Overrided       | 2.319 ms | 0.0216 ms | 0.0202 ms | 31.2500  | 15.6250 | 383.37 KB |
Enter fullscreen mode Exit fullscreen mode

There is also a great blog post that talks about this in detail, it you're interested.

That's it, see you next post.

Top comments (0)