Продолжение теста ServiceStack: почему сохранение простого (сложного) в JSON замедляет SELECT? - PullRequest
0 голосов
/ 28 июня 2018

Итак, mythz не понравился мой не совсем научный вопрос о тестировании в предыдущем вопросе SO, но, поскольку я хотел бы переключиться на OrmLite, мне нужно выяснить, если он медленный, и если да, то почему.

В своем «исследовании» я пришел к выводу, что сложные объекты, которые в OrmLite помечены как JSON, являются виновниками очень медленных SELECT.

Поэтому я создал новый проект, который фокусируется исключительно на OrmLite и не сравнивается ни с чем, кроме себя самого, и цель здесь состоит в том, чтобы увидеть различия между пометкой объектов JSON и отсутствием их.

Его можно найти на GitHub: https://github.com/tedekeroth/ormlitebenchmarking Решение выглядит так:
enter image description here

Я использую OrmLite 5.1.1 на Windows 7, 2,6 ГГц, 24 ГБ ОЗУ и нет Загрузка процессора в настоящее время, используя MySql 5.6. Приложение подключается к 127.0.0.1 (root / root) и нуждается в базе данных "ormlite".

Я включил ThrowOnError:

OrmLiteConfig.ThrowOnError = JsConfig.ThrowOnError = true;

Приложение выглядит так:

enter image description here

  • Нет данных: только созданный объект, никакие свойства не имеют данных enter image description here
  • Примитивы: заполнены только некоторые простые примитивные свойства enter image description here
  • Prim + один комплекс: все примитивы, как указано выше + один объект blobbed комплекс enter image description here
  • Полные данные: все вышеперечисленное + еще 2 сложных объекта blobbed enter image description here

Кнопка Создать сначала создает 10 000 объектов в списке, а затем они сохраняются с помощью метода вставки OrmLite. Измерение времени выполняется только для вставок, а не для создания объектов.

public void AddRow<T>(T coreObject) where T : CoreObject
{
    long id = 0;
    using (var _db = _dbFactory.Open())
    {
        id = _db.Insert<T>(coreObject, selectIdentity: true);
    }           
}

Кнопка Read считывает все строки в таблице и воссоздает объекты Customer:

public List<T> FetchAll<T>()
{
    using (var _db = _dbFactory.Open())
    {
        List<T> list = _db.Select<T>();
        return list;
    }
}

Итак, тестирование должно быть сделано так:

  • Выберите режим и нажмите Создать , отобразится время, необходимое
  • Нажмите Чтение , чтобы прочитать все строки, которые в данный момент находятся в таблице

Чтобы проверить другой режим, очистите таблицу БД (customer), чтобы она была чистой.


СРАВНИТЕЛЬНЫЙ

ВСТАВИТЬ
Создание 10 000 объектов (не измерено) и вставка их в базу данных.

  • Нет данных: ~ 26-27 секунд
    enter image description here
  • Примитивы: ~ 27,1-27,4 секунды
    enter image description here
  • Прим + один комплекс: ~ 27,5-29 секунд
    enter image description here
  • Полные данные: ~ 28 секунд
    enter image description here

Итак, в целом, примерно одинаково, 26-29 секунд.

SELECT
Чтение 10 000 объектов из базы данных, как указано выше.

  • Нет данных: ~ 460 мс
    enter image description here
  • Примитивы: ~ 700-720 мс
    enter image description here
  • прим + один комплекс: ~ 970-1030 мс
    enter image description here
  • Полные данные: 30000-32000 мс (30-32 секунд)
    enter image description here

Выводы

«Полные данные» - это, очевидно, место, где появляется большой привкус. Сложные объекты большого размера, которые добавляются (ContactDetails), похоже, запутали это. Я заметил это в предыдущем тесте, но сам объект не очень сложен, см. Ниже. Поэтому я не уверен, почему он так прыгает или эти цифры разумны.

Вот почему я подумал, что, возможно, Mythz может взглянуть на этот более чистый тест? =)

Вопрос заключается в следующем: почему сохранение объекта (в JSON согласно OrmLite) замедляет SELECT таким образом?

[Serializable]
public class ContactDetails 
{
    public List<ContactItem> ContactItemList
    {
        get; set;
    }
    public ContactItem CurrentContactItem
    {
        get; set; 
    }
    public ContactItem DefaultContactItem
    {
        get; set;
    }
    public bool IgnorePrimaryWaitBuffer
    {
        get; set;
    }

    public ContactDetails(List<ContactItem> contactItemList, ContactItem currentContactItem, ContactItem defaultContactItem)
    {
        ContactItemList = contactItemList;
        CurrentContactItem = currentContactItem;
        DefaultContactItem = defaultContactItem;
    }

    public ContactDetails()
    {
    }
}

1 Ответ

0 голосов
/ 30 июня 2018

Мне удалось загрузить и профилировать это решение, которое высветило причину проблемы, связанной с отсутствием кэширования методов доступа типов с поздним связыванием, которая решается с помощью this commit .

С этим изменением производительность загрузки 10000 строк со сложными типами уменьшена с 11 765 мс до 669 мс (на моем iMac 5k), как показано ниже:

enter image description here

Это изменение доступно с v5.1.1, которое теперь доступно на MyGet .

Примечание: я удалил строку JsConfig.IncludeTypeInfo ниже:

JsConfig.IncludeTypeInfo = true;

Что заставляет сериализаторы выдавать информацию о типе для каждого объекта, что увеличивает размер полезной нагрузки и снижает производительность . ServiceStack.Text уже будет выдавать информацию о типе, когда она необходима, то есть для классов object, interfaces и abstract, поэтому вам следует редко применять ее самостоятельно, если она не является абсолютно необходимой, поскольку это может оказать значительное негативное влияние на производительность. ,

В идеале ваши DTO не должны использовать интерфейсы, объекты с поздним связыванием или наследование, но если вы планируете создавать базовые типы abstract, чтобы принудительно передавать информацию о типе только туда, где она нужна, вместо того, чтобы всегда их излучать.

...