День 1485. #ЗаметкиНаПолях
Особенность (Производительности) JsonSerializer
System.Text.Json.JsonSerializer имеет странную особенность в отношении производительности и управления памятью. Посмотрим, что «не так» с этим кодом:
JsonSerializer.Serialize(myObject,
new JsonSerializerOptions(...));.
JsonSerializerOptions используется для настройки сериализации и десериализации объектов. Он имеет несколько свойств, которые описывают, например, хотите ли вы иметь отступы в JSON. Теперь небольшой бенчмарк:
public record Test(string FirstName, string LastName);
[MemoryDiagnoser]
public class JsonBenchmark
{
private static readonly
JsonSerializerOptions _options = new();
public void Default()
{
for (var i = 0; i < 100; i++)
JsonSerializer.Serialize(
new Test("Steven", "Giesel"));
}
[Benchmark]
public void OptionsField()
{
for (var i = 0; i < 100; i++)
JsonSerializer.Serialize(
new Test("Steven", "Giesel"), _options);
}
[Benchmark]
public void OptionsNew()
{
for (var i = 0; i < 100; i++)
JsonSerializer.Serialize(
new Test("Steven", "Giesel"),
new JsonSerializerOptions());
}
}
Итак, у нас 3 метода. Один без опций, второй использует статическое поле, а третий – каждый раз вызывает new(). Результаты:
| Method | Mean | Allocated |
|--------------|----------|-----------|
| Default | 13.89 us | 14.06 KB |
| OptionsField | 13.89 us | 14.06 KB |
| OptionsNew | 53.36 us | 42.79 KB |
Мы видим, что создание нового экземпляра в цикле - это дорого. Очевидно, что new() требует времени и памяти. Да, но не настолько. На самом деле, если параметры не установлены, используется экземпляр по умолчанию. При передаче параметров сериализатор вызывает функцию InitializeForReflectionSerializer. Она довольно дорогая, поскольку собирает необходимую информацию о том, как выглядит ваш объект с помощью рефлексии. Если мы используем статическое поле, экземпляр инициализируется один раз, а дальше будет кэширован. При использовании new() InitializeForReflectionSerializer вызывается каждый раз.
И что?
Если вы вызываете сериализацию/десериализацию нечасто, ничего страшного. Но вы можете использовать её в Entity Framework в сочетании с ValueConverter:
protected override void OnModelCreating(ModelBuilder mb)
{
mb
.Entity<MyModel>()
.Property(e => e.MyList)
.HasConversion(
v => JsonSerializer
.Serialize(v, new JsonSerializerOptions(…)),
v => JsonSerializer
.Deserialize<List<MyType>>(
v, new JsonSerializerOptions(…)));
}
Если у вас тысячи строк, это может быть очень дорого! Лучше взять значение по умолчанию или создать статическое поле.
Источник: https://steven-giesel.com/blogPost/0e7512b0-c854-4cce-8606-765fcaf1e69c