Delphi: самый быстрый DataSet (TJvMemoryData, TMemTableEh, TdxMemData, TkbmMemTable)

geek.png

В этой статье я проведу сравнительный анализ DataSet’ов, которые держат данные в оперативной памяти.

Список DataSet’ов


  1. TJvMemoryData
    Разработчик: сообщество JEDI Visual Component Library (JCL + JVCL)
    JCL (версия 2.8)
    JVCL (версия 3.50)
    → Официальный сайт
  2. TMemTableEh
    Разработчик: EhLib
    Версия: 9.0.040
    → Официальный сайт
  3. TdxMemData
    Разработчик: DevExpress
    Версия: 15.2.2
    → Официальный сайт
  4. TkbmMemTable
    Разработчки: Components4Developers
    Версия: 7.74.00 Professional Edition
    → Официальный сайт


Параметры сравнения DataSet’ов


  1. Вставка записей
  2. Сортировка записей


Окружение

Delphi 10.2 Tokyo Starter
Операционная система Windows 7 SP1 Ultimate x64
Процессор Intel Core i5
ОЗУ 8 Гб

Тестовые данные


DataSet’ы будут тестироваться на данных, полученных из базы данных Firebird. Для сравнительного анализа я создал в базе данных 100000 записей с различными типами данных:

• целые числа;
• вещественные числа;
• даты;
• строки;
• изображения.

Сравнение


  1. Загрузка данных
    DataSet LoadFromDataSet Ручная Среднее
    TJvMemoryData 7,7846 сек 5,7500 сек 6,7673 сек
    TMemTableEh 4,5114 сек 7,2978 сек 5,9046 сек
    TdxMemData 6,3804 сек 6,5082 сек 6,4443 сек
    TkbmMemTable 5,4474 сек 6,0562 сек 5,7518 сек
    Загрузки для каждого DataSet’a производились по 5 раз, из полученных значений подсчитвывалось среднее арифметическое. Среднее значение — это среднее арифметическое значения загрузки при помощи метода LoadFromDataSet и значения ручной загрузки.

    Результаты:

    TMemTableEh — самая быстрая загрузка данных при помощи метода LoadFromDataSet
    TJvMemoryData — самая быстрая ручная загрузка данных
    TkbmMemTable — самая быстрая средняя загрузка данных

    Исходный код замера времени
    var
      start_time, end_time, total_time: double;
    
    start_time := GetTickCount;
    ...
    end_time   := GetTickCount;
    total_time := (end_time - start_time) / 1000;
    

    Исходный код загрузки данных LoadFromDataSet
    //TJvMemoryData
    //function LoadFromDataSet(Source: TDataSet; RecordCount: Integer; Mode: TLoadMode; DisableAllControls: Boolean = True): Integer;
    JvMemoryData.LoadFromDataSet(FIBDataSet, -1, lmCopy);
    
    //TMemTableEh
    //function LoadFromDataSet(Source: TDataSet; RecordCount: Integer; Mode: TLoadMode; UseCachedUpdates: Boolean): Integer;
    MemTableEh.LoadFromDataSet(FIBDataSet, -1, lmCopy, true);
    
    //TdxMemData
    //procedure LoadFromDataSet(DataSet : TDataSet);
    dxMemData.LoadFromDataSet(FIBDataSet);
    
    //TkbmMemTable
    //procedure TkbmCustomMemTable.LoadFromDataSet(Source:TDataSet; CopyOptions:TkbmMemTableCopyTableOptions; Mapping:string='');
    kbmMemTable.LoadFromDataSet(FIBDataSet, [mtcpoAppend]);
    

    Исходный код ручной загрузки
    while not FIBDataSet.Eof do
    begin
      table.Append;
      table.FieldByName('ID').AsInteger     := FIBDataSet.FieldByName('ID').AsInteger;
      table.FieldByName('SUMM').AsFloat     := FIBDataSet.FieldByName('SUMM').AsFloat;
      table.FieldByName('COMMENT').AsString := FIBDataSet.FieldByName('COMMENT').AsString;
      ...
      table.Post;
    
      FIBDataSet.Next;
    end;
    
    table.First;
    

    TJvMemoryData — единственный DataSet, у которого ручная загрузка данных оказалось быстрее, чем загрузка при помощи метода LoadFromDataSet.

    TdxMemData — единственный DataSet, который после загрузки данных при помощи метода LoadFromDataSet не возвращает позицию в DataSet на первую запись.

  2. Сортировка
    DataSet Целое число Вещественное число Строка Среднее
    TJvMemoryData 0,3492 сек 0,8330 сек 1,9938 сек 1,0587 сек
    TMemTableEh 0,9014 сек 0,8642 сек 3,6876 сек 1,8177 сек
    TdxMemData 0,3616 сек 0,3650 сек 0,9134 сек 0,5467 сек
    TkbmMemTable 0,1996 сек 0,2186 сек 0,7550 сек 0,3897 сек
    Сортировка для каждого DataSet’a производились по 5 раз, из полученных значений подсчитвывалось среднее арифметическое. Среднее значение — это среднее арифметическое значений сортировок.

    Результаты:

    TkbmMemTable — самая быстрая сортировка целых чисел
    TkbmMemTable — самая быстрая сортировка вещественных чисел
    TkbmMemTable — самая быстрая сортировка строк
    TkbmMemTable — самая быстрая средняя сортировка

    Исходный код замера времени
    var
      start_time, end_time, total_time: double;
    
    start_time := GetTickCount;
    ...
    end_time   := GetTickCount;
    total_time := (end_time - start_time) / 1000;
    

    Исходный код сортировки данных
    //TJvMemoryData
    //procedure SortOnFields(const FieldNames: string = ''; CaseInsensitive: Boolean = True; Descending: Boolean = False);
    JvMemoryData.SortOnFields(fields, false, false);
    
    //TMemTableEh
    //procedure SortByFields(const SortByStr: string);
    MemTableEh.SortByFields(fields);
    
    //TdxMemData
    dxMemData.SortedField := fields;
    
    //TkbmMemTable
    //procedure SortOn(const FieldNames:string; Options:TkbmMemTableCompareOptions);
    kbmMemTable.SortOn(fields, []);
    


Отмечу, что только для DataSet’а TMemTableEh при сортировке по нескольким полям можно задавать различное направление сортировки для каждого поля (по возрастанию/по убыванию).

Заключение


Безоговорочным лидером оказался DataSet TkbmMemTable, но и все другие DataSet’ы показали хорошие результаты. Но воспользоваться TkbmMemTable можно только с Delphi XE2.

Конечно, на реальных данных вряд ли понадобиться загружать 100000 записей для отображения пользователю. Так же при выборе DataSet’а нужно учитывать, что на другом наборе данных (например, при отсутствии загрузки изображений или для x64-приложения) результаты скорости работы могут оказаться другими. К тому же многие DataSet’ы имеют дополнительный функционал для работы с гридом из своей библиотеки.

Дополнительно о TMemTableEh


Компонент TMemTableEh появился в версии EhLib 4.0. Все компоненты библиотеки, включая TMemTableEh, работают начиная с Delphi 7.

Преимущества использования MemTableEh по сравнению с другими DataSet’ми следующие:

  1. Поддерживает специальный интерфейс, позволяющий компоненту DBGridEh просматривать все данные, не перемещая активную запись.
  2. Позволяет закачивать в себя данные из объекта TDataDriverEh (свойство DataDriver).
  3. Позволяет выгружать изменения обратно в DataDriver, оперативно или отложено (в зависимости то свойства CachedUpdates).
  4. Позволяет создавать мастер/дитэил отношения на клиенте (фильтруя записи) или на внешнем источнике (обновляя параметры [Params] и перезапрашивая данные c DetailDataDriver’а).
  5. Позволяет сортировать данные, включая Calculated и Lookup поля.
  6. Позволяет создавать и заполнять данные в design-time и сохранять данные в dfm-файле формы.
  7. Позволяет хранить записи в виде дерева. Каждая запись может иметь записи узлы/ветви и сама являться узлом другой родительской записи. Компонент TDBGridEh поддерживает функциональность отображения древовидной структуры этих записей.
  8. Позволяет подключиться к внутреннему массиву другого компонента TMemTableEh (свойство ExternalMemData) и работать с его данными: сортировать, фильтровать, редактировать.
  9. Имеет интерфейс для получения списка всех значений столбца, игнорируя локальный фильтр DataSet’а. TDBGridEh использует это свойство для автоматического формирования списка в выпадающем DropDownBox’е фильтра.


О TRxMemoryData


Разработчки: RxLib
Компонент TRxMemoryData появился в версии RxLib 2.60.

DataSet TRxMemoryData не участвует в сравнении, потому что в 2002 году библиотека RxLib была официально включена в состав JVCL. В JVCL есть утилита для быстрой замены всех Rx компонентов, функций и юнитов на JVCL-версии.

Почему стоит перейти на JVCL:

В отличие от RxLib, JVCL развивается. Ошибки исправляются. Регулярно выходят версии с поддержкой новых версий Delphi. JVCL компоненты поддерживают новые версии Windows и Windows-стили.

Порядок использования DataSet’ов в моей практике


  1. TRxMemoryData
  2. TMemTableEh
  3. TRxMemoryData
  4. TJvMemoryData
  5. TkbmMemTable

© Geektimes