.NET JsonSerializer is over 200x faster if you reuse JsonSerializerOptions

When you serialize an object using the built-in .NET JsonSerializer (System.Text.Json), reusing the JsonSerializerOptions object is 200x faster than serializing with a new options object each time.

The JsonSerializerOptions object must have some kind of internal caching mechanism that makes subsequent serializations on the same type significantly faster. Note: Creating the new JsonSerializerOptions object adds insignificant time to the total cost of serialization. I took account of that when comparing the serialization speeds. Even if you disregard the time it takes to new up the object, reusing the options object is still 200x faster in subsequent serializations.

In this article I’ll show a speed comparison of serializing with and without reusing JsonSerializerOptions.

Speed comparison results – reusing vs not reusing JsonSerializerOptions

To compare speeds, I serialized the same object 100 times in a loop, then took the average, min, max, first time, and average without first time.

You may think newing up the JsonSerializerOptions object could be the reason why the reusing approach is so much faster, but that’s not the case. That adds, on average, 0.02 ms, which has virtually no impact on the total time it takes to serialize.

Serializing an object for the first time is always slow. That’s why I’m comparing the average time without first serialization.

Speed comparison table:

Average Time Without First Serialization
Approach 1 – Not reusing the JsonSerializerOptionsCreating options object: 0.0221 ms
Serializing: 3.043 ms
Total: 3.0651 ms
Approach 2 – Reusing JsonSerializerOptions0.0145 ms

3.0651 ms / 0.0145 ms = 211.

Approach 2 – reusing the JsonSerializerOptions – is 211x faster than not reusing the options.

I would speculate that the JsonSerializerOptions contains some internal caching mechanism that makes subsequent serializations of the same type significantly faster. But how it works doesn’t matter that much. If you are doing repeated serializations, always reuse the options object.

Approach 1 – Speed test – Serializing and NOT reusing JsonSerializerOptions

  • using System.Text.Json;
List<double> nonCachingOptionTimes = new List<double>(); List<double> timeForCreatingNewOptions = new List<double>(); Stopwatch sw = new Stopwatch(); for (int i = 0; i < 100; i++) { sw.Restart(); var options = new JsonSerializerOptions() { WriteIndented = true }; options.Converters.Add(new JsonStringEnumConverter()); timeForCreatingNewOptions.Add(sw.Elapsed.TotalMilliseconds); sw.Restart(); var json = JsonSerializer.Serialize(nflTeam, options); sw.Stop(); nonCachingOptionTimes.Add(sw.Elapsed.TotalMilliseconds); } Console.WriteLine($"no caching - newing up options. min={timeForCreatingNewOptions.Min()} max={timeForCreatingNewOptions.Max()} avg={timeForCreatingNewOptions.Average()}"); Console.WriteLine($"no caching - serializing. first={nonCachingOptionTimes.First()} min={nonCachingOptionTimes.Min()} max={nonCachingOptionTimes.Max()} avg={nonCachingOptionTimes.Average()} avgWithoutFirst={nonCachingOptionTimes.Skip(1).Average()}");

Creating the options object:

  • Min=0.0024 ms
  • Max=1.8253 ms
  • Avg=0.0221 ms

Serializing:

  • First=43.0357 ms
  • Min=2.4857 ms
  • Max=43.036 ms
  • Avg=3.4436 ms
  • AvgWithoutFirst=3.043 ms

Total (creating new options object + serializing) average without first: 0.0221 ms + 3.043 ms = 3.0651 ms

Approach 2 – Speed Test – Serializing and reusing JsonSerializerOptions

  • using System.Text.Json;
var cachedOption = new JsonSerializerOptions() { WriteIndented = true }; cachedOption.Converters.Add(new JsonStringEnumConverter()); List<double> cachedOptionTimes = new List<double>(); Stopwatch sw = new Stopwatch(); for (int i = 0; i < 100; i++) { sw.Restart(); var json = JsonSerializer.Serialize(nflTeam, cachedOption); sw.Stop(); cachedOptionTimes.Add(sw.Elapsed.TotalMilliseconds); } Console.WriteLine($"caching. first={cachedOptionTimes.First()} min={cachedOptionTimes.Min()} max={cachedOptionTimes.Max()} avg={cachedOptionTimes.Average()} avgWithoutFirst={cachedOptionTimes.Skip(1).Average()}");

Serializing:

  • First=45.39 ms
  • Min=0.0107 ms
  • Max = 45.39 ms
  • Avg = 0.4678 ms
  • Average without first = 0.0145 ms

NFLTeam – object used for serialization tests

{ var team = new NFLTeam() { City = "Detroit", Name = "Lions", Conference = Conferences.NFC, Divison = Divisions.North, HeadCoach = new Person() { FirstName = "Matt", LastName = "Patricia" }, Stats = new Stats() { RegularSeasonWins = 559, RegularSeasonLosses = 658, RegularSeasonTies = 32, PlayoffWins = 7, PlayoffLosses = 13, SuperBowlWins = 0, SuperBowlLosses = 0 }, Players = new List<Player>() { new Player() { FirstName = "Matthew", LastName = "Stafford", Position = PlayerPositions.QB, YearsOfExperience = 12, College = "Georgia" }, new Player() { FirstName = "Kenny", LastName = "Golladay", Position = PlayerPositions.WR, YearsOfExperience = 4, College = "Northern Illinois" }, new Player() { FirstName = "Tracy", LastName = "Walker", Position = PlayerPositions.DB, YearsOfExperience = 3, College = "Louisiana-Lafayette" }, new Player() { FirstName = "T.J.", LastName = "Hockenson", Position = PlayerPositions.TE, YearsOfExperience = 2, College = "Iowa" } } }; return team; }

Leave a Comment