- Frugal Cafe
- Posts
- FC13 Reuse contract resolver objects
FC13 Reuse contract resolver objects
Lawyers are expensive
Lots of serialization/deserialization code are now implemented using reflection, code generation, then code jitting, to improve runtime performance. All these steps could be very expensive, so the only effective solution is to generate them once, cache them, and reuse over and over again. When using serialization implementation, it’s super important to make sure such caching is working properly.
NewtonSoftJson is a commonly used library for JSON serialization/deserialization. Its cache is stored inside contract resolver object. When you use its default API, only a single instance of DefaultContractResolver is getting used, which stores all such expensive information. Everything should work fine after the first iteration for each unique type involved.
But things could get costly if you allocate your own contract resolver objects. Here is a simple test case:
Here we’re comparing the performance of two serialization calls. The first uses the null setting, so singleton default contract resolver is getting used. The second one uses custom serialization setting which allocates contract resolver object every time.
Here are test results:
The first case finishes in mere 3.84 µs with 1,461 bytes allocation. The second case finishes in 674 µs, with 65,712 bytes allocation. It’s just night and day.
Here is from CPU trace for the second case:
Most CPU is spent inside ResolveContract, here mainly due to reflection API cost.
Similar issue happens with System.Text.Json for its earlier versions in options object.
Contract resolver objects are just like lawyers which are expensive to hire, but we can reuse the contracts generated by them, not retire them every time.