|
Не так давно на этом сайте были размещены результаты тестирования вариантов доступа к данным на примере Rainbow и технологии ADO. Тестирование проводилось на достаточно сложных запросах, которые нечасто встречаются в работе с базой данных. Результаты его (которые можно посмотреть здесь) заинтересовали. Вместе с тем было принято решение не ограничиваться только этими вариантами доступа к базе данных, а расширить список. Кроме того было принято решение выполнить в качестве теста достаточно типовые задачи, например типичный запрос к регистру остатков для построения отчета.
В данном тестировании будут принимать участие следующие методы доступа к данным:
- стандартный доступ путем выполнения запроса 1С;
- прямой запрос к данным с помощью Rainbow;
- прямой запрос к данным с помощью технологии ADO
;
- прямой запрос к данным с помощью 1C++;
- прямой запрос к данным с помощью внешней компоненты Neta
.dll
Параметры тестирования:
Будет использоваться самописная база на компоненте "Тогровля". База содержит информацию за период 2,5 года. Перед тестированием базы данных, она была сжата («Shrink Database»). Объем базы данных, отображаемый в "EM", составляет 465 Мб. Тестовой задачей будет построение стандартного отчета по регистру "Партии" с периодом с "05.02.2002" по "20.01.2004" (то есть почти за два года). В первом варианте будет задано условие построения отчета по двум местам хранения (без группирования по местам хранения, простое суммирование данных). Второй вариант построяния отчета - без всяких условий.
В запросе должна быть единственная группировка по товару. В отчет выводятся начальные и конечные остатки за период, а также приход и расход товаров в количественном выражении. Печатная форма выводится. Замер времени работы будет производиться с помощью недокументированной функции 1С:
GetPerformanceCounter().
Тестовая платформа:
Аппаратная часть: Атлон-1400 512 Мб DDR, HDD 2*40Гб IDE Raid-0.
Программная часть: Win2003 server, SQL 2000 server sp3, 1С (21 релиз).
В качестве тестового вопроса будет использоваться стандартный запрос по регистру партий товаров. Посмотреть текст запроса (вариант без фильтра по местам хранения), а заодно и прочитать статью можно здесь. Запрос с фильтром по двум местам хранения не приводится с целью экономии места и времени. Внимание: отличие тестового запроса от приведенного в том, что результирующая таблица в тестовом запросе еще соединяется со справочником товаров, группируется по наименованию и сортируется по возрастанию. Таким образом запрос возвращает наименование товара, а не ID объекта.
Код модуля отчета для тестирования представлен ниже, думаю по названиям процедур видно, какая за что отвечает...
Перем НачДата,КонДата; Перем Магазин1, Магазин2; Перем ТекстЗапросаРадуга, ТекстЗапросаАДО;
//________________________________________________________ Процедура Выполнить1С() НачЧисло=_GetPerformanceCounter(); //начало теста Запрос=СоздатьОбъект("Запрос"); СЗ=СоздатьОбъект("СписокЗначений"); СЗ.ДобавитьЗначение(Магазин1); СЗ.ДобавитьЗначение(Магазин2); ТекстЗапроса="//{{ЗАПРОС(ОдинС) |Период с НачДата по КонДата; |Магазин = Регистр.Партии.Магазин; |Наименование = Регистр.Партии.Товар; |Количество = Регистр.Партии.Количество; |Функция Нач = НачОст(Количество); |Функция Прих = Приход(Количество); |Функция Расх = Расход(Количество); |Функция Кон = КонОст(Количество); |Группировка Наименование без групп; |Условие(Магазин в СЗ); |"//}}ЗАПРОС ; Если Запрос.Выполнить(ТекстЗапроса)=0 Тогда Предупреждение("Не выполнился запрос!",10); Возврат; КонецЕсли; Таб=СоздатьОбъект("Таблица"); Таб.ИсходнаяТаблица("Таблица"); Таб.ВывестиСекцию("Заголовок"); Пока Запрос.Группировка(1)=1 Цикл ТекИмя=Запрос.Наименование; ТекНач=Запрос.Нач; ТекПрих=Запрос.Прих; ТекРасх=Запрос.Расх; ТекКон=Запрос.Кон; Таб.ВывестиСекцию("Строка"); КонецЦикла; Таб.Опции(0,0,0,0); Таб.ТолькоПросмотр(1); Таб.Показать(); Запрос=""; //конец теста КонЧисло=_GetPerformanceCounter(); ТекРезультат=КонЧисло-НачЧисло; Сообщить(ТекРезультат); КонецПроцедуры
//________________________________________________________ Процедура ВыполнитьРадуга() НачЧисло=_GetPerformanceCounter(); //начало теста ЗапросРадуги=СоздатьОбъект("ODBCQuery"); Таб=СоздатьОбъект("Таблица"); Таб.ИсходнаяТаблица("Таблица"); Таб.ВывестиСекцию("Заголовок"); Если ЗапросРадуги.Prepare(ТекстЗапросаРадуга,1,1)=1 Тогда Если ЗапросРадуги.Open()=1 Тогда ЗапросРадуги.GotoNext(); Пока ЗапросРадуги.IsOK()=1 Цикл //Считываем данные ТекИмя=ЗапросРадуги.GetString(0); ТекНач=ЗапросРадуги.GetString(1); ТекПрих=ЗапросРадуги.GetString(2); ТекРасх=ЗапросРадуги.GetString(3); ТекКон=ЗапросРадуги.GetString(4); Таб.ВывестиСекцию("Строка"); //**************** ЗапросРадуги.GotoNext(); КонецЦикла; ЗапросРадуги.Close(); Иначе Предупреждение("Ошибка открытия запроса!",10); КонецЕсли; ЗапросРадуги.Reset(); Иначе Предупреждение("Ошибка выполнения запроса!",10); КонецЕсли; ЗапросРадуги=""; Таб.Опции(0,0,0,0); Таб.ТолькоПросмотр(1); Таб.Показать(); //конец теста КонЧисло=_GetPerformanceCounter(); ТекРезультат=КонЧисло-НачЧисло; Сообщить(ТекРезультат); КонецПроцедуры
//________________________________________________________ Процедура ВыполнитьАДО() //начало теста Если МонопольныйРежим()=1 Тогда Предупреждение("Невозможно выполнить запрос в монопольном режиме!",10); //Действия Возврат; //******** КонецЕсли; Соединение=СоздатьОбъект("ADODB.Connection"); ТекСервер="***"; ТекПароль="***"; ТекБаза="***"; СтрокаКоннекта="driver={SQL Server};server="+ТекСервер+";uid=sa;pwd="+ТекПароль+";Database="+ТекБаза; Соединение.ConnectionTimeOut=20; Соединение.CursorLocation=3; Попытка Соединение.Open(СтрокаКоннекта); Исключение Предупреждение("Невозможно установить соединение с базой данных!"); //Действия
//******** КонецПопытки; Таб=СоздатьОбъект("Таблица"); Таб.ИсходнаяТаблица("Таблица"); Таб.ВывестиСекцию("Заголовок"); ЗапросАДО=СоздатьОбъект("ADODB.Command"); ЗапросАДО.ActiveConnection=Соединение; НачЧисло=_GetPerformanceCounter(); ЗапросАДО.CommandText=ТекстЗапросаАДО; Выборка=ЗапросАДО.Execute(); Если Выборка.EOF()=-1 Тогда Иначе Выборка.MoveFirst(); Пока Выборка.EOF()=0 Цикл //Обработка выборки данных ТекИмя=Выборка.Fields(0).Value; ТекНач=Выборка.Fields(1).Value; ТекПрих=Выборка.Fields(2).Value; ТекРасх=Выборка.Fields(3).Value; ТекКон=Выборка.Fields(4).Value; Таб.ВывестиСекцию("Строка"); //***************** Выборка.MoveNext(); КонецЦикла; КонецЕсли; Выборка.Close(); Соединение.Close(); Выборка=""; ЗапросАДО=""; Соединение=""; Таб.Опции(0,0,0,0); Таб.ТолькоПросмотр(1); Таб.Показать(); //конец теста КонЧисло=_GetPerformanceCounter(); ТекРезультат=КонЧисло-НачЧисло; Сообщить(ТекРезультат); КонецПроцедуры
//________________________________________________________ Процедура Выполнить1Спп() НачЧисло=_GetPerformanceCounter(); //начало теста Запрос=СоздатьОбъект("ODBCRecordSet"); Запрос.Открыть(ТекстЗапросаРадуга); Запрос.УстТипыКолонок1С("Строка,Число,Число,Число,Число"); ТЗ=СоздатьОбъект("ТаблицаЗначений"); Запрос.ПолучитьРезультатыВ_ТЗ(ТЗ,1); Запрос.Закрыть(); Таб=СоздатьОбъект("Таблица"); Таб.ИсходнаяТаблица("Таблица"); Таб.ВывестиСекцию("Заголовок"); ТЗ.ВыбратьСтроки(); Пока ТЗ.ПолучитьСтроку()=1 Цикл ТекИмя=ТЗ.Имя; ТекНач=ТЗ.Нач; ТекПрих=ТЗ.Прих; ТекРасх=ТЗ.Расх; ТекКон=ТЗ.Кон; Таб.ВывестиСекцию("Строка"); КонецЦикла; Таб.Опции(0,0,0,0); Таб.ТолькоПросмотр(1); Таб.Показать(); //конец теста КонЧисло=_GetPerformanceCounter(); ТекРезультат=КонЧисло-НачЧисло; Сообщить(ТекРезультат); КонецПроцедуры
//________________________________________________________ Процедура ВыполнитьНета() НачЧисло=_GetPerformanceCounter(); //начало теста Запрос=СоздатьОбъект("AddIn.NPeriodik"); Таб=СоздатьОбъект("Таблица"); Таб.ИсходнаяТаблица("Таблица"); Таб.ВывестиСекцию("Заголовок"); ТЗ=СоздатьОбъект("ТаблицаЗначений"); ТЗ.НоваяКолонка("Имя"); ТЗ.НоваяКолонка("Нач"); ТЗ.НоваяКолонка("Прих"); ТЗ.НоваяКолонка("Расх"); ТЗ.НоваяКолонка("Кон"); ТЗПарм=СоздатьОбъект("ТаблицаЗначений"); ТЗПарм.НоваяКолонка("Номер"); ТЗПарм.НоваяКолонка("Тип"); ТЗПарм.НоваяКолонка("Размер"); ТЗПарм.НоваяСтрока(); ТЗПарм.Номер=1; ТЗПарм.Тип=1; ТЗПарм.Размер=0; ТЗПарм.НоваяСтрока(); ТЗПарм.Номер=2; ТЗПарм.Тип=4; ТЗПарм.Размер=4; ТЗПарм.НоваяСтрока(); ТЗПарм.Номер=3; ТЗПарм.Тип=4; ТЗПарм.Размер=4; ТЗПарм.НоваяСтрока(); ТЗПарм.Номер=4; ТЗПарм.Тип=4; ТЗПарм.Размер=4; ТЗПарм.НоваяСтрока(); ТЗПарм.Номер=5; ТЗПарм.Тип=4; ТЗПарм.Размер=4; //________________________________________________________ Запрос.ЗапросК1С(ТекстЗапросаРадуга,ТЗПарм,ТЗ,2); ТЗ.ВыбратьСтроки(); Пока ТЗ.ПолучитьСтроку()=1 Цикл ТекИмя=ТЗ.Имя; ТекНач=ТЗ.Нач; ТекПрих=ТЗ.Прих; ТекРасх=ТЗ.Расх; ТекКон=ТЗ.Кон; Таб.ВывестиСекцию("Строка"); КонецЦикла; Таб.Опции(0,0,0,0); Таб.ТолькоПросмотр(1); Таб.Показать(); //конец теста КонЧисло=_GetPerformanceCounter(); ТекРезультат=КонЧисло-НачЧисло; Сообщить(ТекРезультат); КонецПроцедуры
//________________________________________________________ Процедура ПриОткрытии() НачДата='05.02.2002'; КонДата='20.01.2004'; Спр=СоздатьОбъект("Справочник.Магазины"); Спр.НайтиПоКоду("1"); Магазин1=Спр.ТекущийЭлемент(); Спр.НайтиПоКоду("3"); Магазин2=Спр.ТекущийЭлемент(); Если ЗагрузитьВнешнююКомпоненту("1cpp.dll")=0 тогда Предупреждение ("Компонента 1с++ не найдена"); Форма.кнПлюс.Доступность(0); КонецЕсли; Если ЗагрузитьВнешнююКомпоненту("neta.dll")=0 тогда Предупреждение ("Компонента neta не найдена"); Форма.кнНета.Доступность(0); КонецЕсли; КонецПроцедуры
Тестирования проводилось по 3 раза для каждого варианта. Перед началом тестирования все варианты прогонялись, чтобы все необходимые данные закэшировались в оперативной памяти. Время теста дано в миллисекундах.
Ниже приведена таблица для режима тестирования с фильтром по двум местам хранения:
| № |
Наименование. |
| 1C |
Rainbow |
ADO |
1С++ |
Neta.dll |
| 1 |
36298 |
4243 |
4302 |
3810 |
3802 |
| 2 |
35838 |
4219 |
4238 |
3797 |
3800 |
| 3 |
36060 |
4203 |
4226 |
3753 |
3798 |
| Среднее: |
36065 |
4222 |
4255 |
3787 |
3800 |
Ниже приведена таблица для режима тестирования без фильтров:
| № |
Наименование. |
| 1C |
Rainbow |
ADO |
1С++ |
Neta.dll |
| 1 |
149338 |
6502 |
6429 |
5889 |
6004 |
| 2 |
155806 |
6389 |
6437 |
5880 |
5998 |
| 3 |
151409 |
6451 |
6393 |
5889 |
5995 |
| Среднее: |
152184 |
6447 |
6420 |
5886 |
5999 |
Итак, объявляем победителей. Первое место достается 1C++, второе Neta.dll, третье Rainbow.
Также хотелось бы обратить внимание на некоторые закономерности, выявленные тестированием. При отключении фильтра по местам хранения время формирования прямого запроса увеличилось в 1,5 раза. 1С при этом стала затрачивать почти в 4,5 раза больше времени. Это показывает нам, что производительность стандартного запроса 1С деградирует гораздо более высокими темпами при росте выборки, чем производительность прямого запроса.
Перепечатка, воспроизведение в любой форме, распространение, в том числе в переводе, любых материалов с сайта www.softpoint.ru возможны только с письменного разрешения компании "СофтПоинт". Это правило действует для всех без исключения случаев, кроме тех, когда в материале прямо указано разрешение на копирование (основание: Закон Российской Федерации "Об авторском праве и смежных правах").
|