Не так давно на этом сайте были размещены результаты тестирования вариантов доступа к данным на примере 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 возможны только с письменного разрешения компании "СофтПоинт". Это правило действует для всех без исключения случаев, кроме тех, когда в материале прямо указано разрешение на копирование (основание: Закон Российской Федерации "Об авторском праве и смежных правах").
|