Delphi: самый быстрый DataSet (TJvMemoryData, TMemTableEh, TdxMemData, TkbmMemTable)
В этой статье я проведу сравнительный анализ DataSet’ов, которые держат данные в оперативной памяти.
Список DataSet’ов
- TJvMemoryData
Разработчик: сообщество JEDI Visual Component Library (JCL + JVCL)
JCL (версия 2.8)
JVCL (версия 3.50)
→ Официальный сайт - TMemTableEh
Разработчик: EhLib
Версия: 9.0.040
→ Официальный сайт - TdxMemData
Разработчик: DevExpress
Версия: 15.2.2
→ Официальный сайт - TkbmMemTable
Разработчки: Components4Developers
Версия: 7.74.00 Professional Edition
→ Официальный сайт
Параметры сравнения DataSet’ов
- Вставка записей
- Сортировка записей
Окружение
Delphi | 10.2 Tokyo Starter |
Операционная система | Windows 7 SP1 Ultimate x64 |
Процессор | Intel Core i5 |
ОЗУ | 8 Гб |
Тестовые данные
DataSet’ы будут тестироваться на данных, полученных из базы данных Firebird. Для сравнительного анализа я создал в базе данных 100000 записей с различными типами данных:
• целые числа;
• вещественные числа;
• даты;
• строки;
• изображения.
Сравнение
- Загрузка данных
Загрузки для каждого DataSet’a производились по 5 раз, из полученных значений подсчитвывалось среднее арифметическое. Среднее значение — это среднее арифметическое значения загрузки при помощи метода LoadFromDataSet и значения ручной загрузки.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 сек Результаты:
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 на первую запись.
- Сортировка
Сортировка для каждого DataSet’a производились по 5 раз, из полученных значений подсчитвывалось среднее арифметическое. Среднее значение — это среднее арифметическое значений сортировок.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 сек Результаты:
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’ми следующие:
- Поддерживает специальный интерфейс, позволяющий компоненту DBGridEh просматривать все данные, не перемещая активную запись.
- Позволяет закачивать в себя данные из объекта TDataDriverEh (свойство DataDriver).
- Позволяет выгружать изменения обратно в DataDriver, оперативно или отложено (в зависимости то свойства CachedUpdates).
- Позволяет создавать мастер/дитэил отношения на клиенте (фильтруя записи) или на внешнем источнике (обновляя параметры [Params] и перезапрашивая данные c DetailDataDriver’а).
- Позволяет сортировать данные, включая Calculated и Lookup поля.
- Позволяет создавать и заполнять данные в design-time и сохранять данные в dfm-файле формы.
- Позволяет хранить записи в виде дерева. Каждая запись может иметь записи узлы/ветви и сама являться узлом другой родительской записи. Компонент TDBGridEh поддерживает функциональность отображения древовидной структуры этих записей.
- Позволяет подключиться к внутреннему массиву другого компонента TMemTableEh (свойство ExternalMemData) и работать с его данными: сортировать, фильтровать, редактировать.
- Имеет интерфейс для получения списка всех значений столбца, игнорируя локальный фильтр 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’ов в моей практике
- TRxMemoryData
- TMemTableEh
- TRxMemoryData
- TJvMemoryData
- TkbmMemTable