Reusing JsonSerializerOptions (from System.Text.Json) is optimal for performance. It caches type info, which results in a 200x speedup when it deals with the type again. Therefore, always try to reuse JsonSerializerOptions.
I’ll show a speed comparison of serializing with and without reusing JsonSerializerOptions.
Measuring the performance gains of reusing JsonSerializerOptions
To measure the performance gains of reusing JsonSerializerOptions, I compared serializing 100 objects with two approaches:
- Approach 1 – Create a new JsonSerializerOptions object for each serialization operation.
- Approach 2 – Reuse a single JsonSerializerOptions object for all 100 serialization operations.
Serializing an object for the first time is always slow. That’s why I compared the average time without the first serialization.
Here are the performance test results:
Approach | Average serialization time |
Not reusing JsonSerializerOptions | 3.0651 ms |
Reusing JsonSerializerOptions | 0.0145 ms |
This means reusing JsonSerializerOptions makes serialization over 200x faster (3.0651 ms / 0.00145 ms).
Creating the JsonSerializerOptions object only accounts for about 0.02 ms on average. That’s nothing compared to the overall time. The major speedup is thanks to JsonSerializerOptions caching type info.
Now I’ll show the code used for the performing these comparisons.
Approach 1 – Using a new JsonSerializerOptions each time
Here’s the performance test code for approach 1. This loops over 100 objects and serializes them. It creates a new JsonSerializerOptions object each time.
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()}");
Code language: C# (cs)
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 – Reusing JsonSerializerOptions
Here’s the performance test code for approach 2. This creates a single JsonSerializerOptions object and uses it while serializing 100 objects.
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()}");
Code language: C# (cs)
Serializing:
- First=45.39 ms
- Min=0.0107 ms
- Max = 45.39 ms
- Avg = 0.4678 ms
- Average without first = 0.0145 ms
For reference – The object used for performance tests
Here is the object I used in the performance 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;
}
Code language: C# (cs)