Обложка канала

.NET Разработчик

Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин

.NET Разработчик

3 года назад
Открыть в
День 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