Как ускорить работу ert-а?

1. vitebsk13 14.08.14 17:17 Сейчас в теме
Есть ert, который ищет элементы справочника по вхождению в их название заданных пользователем символов и выводит найденное на экран. Примерно так:
Спр=СоздатьОбъект("Справочник.Номенклатура");
Если ВвестиСтроку(стр,"Введите наименование для поиска")=1 тогда
Спр.ВыбратьЭлементы();
Пока спр.ПолучитьЭлемент()=1 цикл
если спр.Этогруппа()=1 тогда
продолжить;
конецесли;
Если Найти(спр.текущийэлемент().наименование,сокрлп(стр))=1 тогда 
тз.новаястрока();
тз.товар=спр.ТекущийЭлемент();
КонецЦикла;
Конецесли;
Показать


Справочник порядка 15 тыс. элементов, один поиск занимает около 20 сек.
Как или чем это ускорить?
Ответы
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
2. Cooler 22 14.08.14 17:36 Сейчас в теме
(1)
ищет элементы справочника по вхождению в их название заданных пользователем символов
Судя по коду, это должно быть вхождение с 1-й позиции, т.е. с начала наименования. Вы действительно это так задумали?
3. Obvious 14.08.14 17:40 Сейчас в теме
как вариант выводить представление() ,а не весь елемент
4. Cooler 22 14.08.14 17:50 Сейчас в теме
(3) Ну, немножко "причесать" код можно разными способами: сократить количество строк, убрать лишний .ТекущийЭлемент() и вычисления в цикле:

Спр=СоздатьОбъект("Справочник.Номенклатура");
Если ВвестиСтроку(стр,"Введите наименование для поиска")=1 тогда
   СокрСтр=СокрЛП(стр);
   Спр.ВыбратьЭлементы();
   Пока спр.ПолучитьЭлемент()=1 цикл
      Если спр.Этогруппа()=0 тогда
         Если Найти(спр.наименование,СокрСтр)>0 тогда 
            тз.новаястрока();
            тз.товар=спр.ТекущийЭлемент();
         КонецЕсли;
      КонецЕсли;
   КонецЦикла;
Конецесли;
Показать


А радикально улучшить быстродействие - через ВК или прямые запросы SQL.
6. vcv 89 14.08.14 20:39 Сейчас в теме
(4) Cooler,
Подозреваю, что ваш вариант прилично ускорится, если строки
Если спр.Этогруппа()=0 тогда
Если Найти(спр.наименование,СокрСтр)>0 тогда
переставить местами.
Ну и не мешало бы наименование и образец поиска приводить к верхнему регистру, что бы "Батон" и "батон" были совпадением.
8. Cooler 22 14.08.14 23:02 Сейчас в теме
(6)
Подозреваю, что ваш вариант прилично ускорится, если строки
Если спр.Этогруппа()=0 тогда
Если Найти(спр.наименование,СокрСтр)>0 тогда
переставить местами.
Очень даже может быть, это я не додумал.

Разве что кроме случая, когда групп в справочнике больше, чем элементов и большинство наименований групп включают строку поиска.

Вероятность этого крайне мала, так что плюсую.

Ну и не мешало бы наименование и образец поиска приводить к верхнему регистру
А вот целесообразность этого зависит от точной постановки задачи, чем автор нас не балует.
9. AlexInqMetal 77 14.08.14 23:38 Сейчас в теме
(8) Cooler, автор нас балует, просто не сразу видно надпись сверху
SSD не помогает, процессор помощнее?
начинаю телепатировать что база файловая по сети)
10. Cooler 22 14.08.14 23:45 Сейчас в теме
(9) Угу, глаз привычно скользит мимо.

Тогда терминал.
11. vcv 89 15.08.14 06:08 Сейчас в теме
(9) AlexInqMetal,
Черт! Я надпись "SSD не помогает, процессор помощнее?" искал долго. Это что за фигня, незаметно болтающаяся между крупно выделенным заголовком (темой) и первым сообщением?
5. МихаилМ 14.08.14 18:11 Сейчас в теме
для поиска по первым символам, можно в запросе указать Условие ( наименование >= искстрока) Условие ( наименование < искстрока2 )

где искстрока2 заменой последнего символа на символ с кодом на 1 больше.

Пример: если нужно найти абв то искстрока = "абв"; искстрока2 = "абг";

для скл версии выгрыш в скорости будет ощутимый.

для дбф - не знаю.
7. vcv 89 14.08.14 20:40 Сейчас в теме
На SQL базах обычно быстрей выбрать запросом элементы справочника с их наименованиями, чем перебирать элементы.
12. _Z1 38 15.08.14 08:53 Сейчас в теме
(7) на sql самый быстрый вариант like b 1c++ ,
для dbf наверное тоже.

ну и сокрлп(стр) можно вынести из цикла.
30. _Z1 38 15.08.14 14:32 Сейчас в теме
И, уж если занудствовать по поводу оптимизации до упора,

смотри (12) - ничего быстрее не придумаешь
40. Cooler 22 15.08.14 22:55 Сейчас в теме
(30)
смотри (12) - ничего быстрее не придумаешь
Смотри (23) - вряд ли твой совет хоть чем-то полезен человеку, написавшему (22).
41. vitebsk13 16.08.14 00:01 Сейчас в теме
(40) Cooler,
ТОвар=СПрТов.ТекущийЭлемент();
действительно принесло результат, т.к. я не стал весь код сюда писать, далее за
тз.НоваяСтрока()
было
тз.цена=СПрТов.ТекущийЭлемент().цена;
тз.производитель=СПрТов.ТекущийЭлемент().Производитель
и т.д. поэтому в моем таки случае было нужно. Прошу прощения за ввод в заблуждение путем выкладки неполного кода.
42. Cooler 22 16.08.14 00:10 Сейчас в теме
(41)
поэтому в моем таки случае было нужно
Ффух!

А теперь попробуйте:
тз.цена=СПрТов.цена;
тз.производитель=СПрТов.Производитель;


Surprise!

P.S. Хотя ХЗ, что там у вас за "и т.д.", может, и нужно. Но сомневаюсь я, функция ТекущийЭлемент() имеет узко специфическое назначение - передать ссылку на элемент справочника в какую-либо процедуру или функцию. А для обращения к элементу выборки и его реквизитам она нужна лишь для... замедления работы программы, что у вас лучше всего остального получается.
43. vitebsk13 16.08.14 12:18 Сейчас в теме
13. vitebsk13 15.08.14 10:03 Сейчас в теме
Всем спасибо, база dbf пользователи сидят через терминалы, попробую изменить код как посоветовали)
14. vitebsk13 15.08.14 10:12 Сейчас в теме
Все сделал. Результат - время поиска до оптимизации кода:27сек, после:10сек. Отлично) Спасибо помогающим! Еще ускориться как-то можно? Железом?
15. Улугбек 15.08.14 10:27 Сейчас в теме
(14) vitebsk13, только запросом.

Либо надо определиться в какой области справочника искать. Например, поиск только внутри определенной группы ускорит процесс. Сейчас у тебя перебирается весь справочник. Более логично искать что-то в нужном месте, а не везде. Например: искать болты лучше в папке "метизы", если таковая иерархия имеет место быть.
16. vitebsk13 15.08.14 10:33 Сейчас в теме
(15) Улугбек, спасибо, надо подумать над этим...
17. _Z1 38 15.08.14 10:50 Сейчас в теме
(14) покажи окончательный текст.
может его тоже можно еще оптимизировать.
AlexInqMetal; +1 Ответить
20. Cooler 22 15.08.14 11:20 Сейчас в теме
(17)
покажи окончательный текст
А к нему - скриншот замера производительности в отладчике.
18. AlexInqMetal 77 15.08.14 11:01 Сейчас в теме
(14) vitebsk13, ну во-первых непонятно каким советам вы последовали так что присмотритесь к (17), во-вторых чтобы посоветовать вам что то по железу, неплохо бы указать его конфигурацию и нагрузку (кол-во пользователей 1с, доп сервисы винды на данном сервере, например установленный sql даст такую нагрузку на диск что 1с нервно курит в сторонке). А так могу сказать чем мощнее - тем лучше, ставьте аппаратный райд 5 или 10 с большим кэшем на ссд высокой надежности, чем больше-тем лучше, и xeon E3 или E5 в зависимости от количества пользователей.
19. pvase 401 15.08.14 11:03 Сейчас в теме
Попробуйте, может так будет быстрее:
Если ВвестиСтроку(стр,"Введите наименование для поиска",100) = 1 тогда
		СокрСтр = Нрег(СокрЛП(стр));
		
		Запрос = СоздатьОбъект("Запрос");
		ТекстЗапроса = 
		"//{{ЗАПРОС(Сформировать)
		|Номенклатура = Справочник.Номенклатура.ТекущийЭлемент;
		|Наименование = Справочник.Номенклатура.Наименование;
		|Группировка Номенклатура без групп;
		//|Группировка Наименование;
		|Условие(Найти(Нрег(Наименование),СокрСтр)>0);
		|"//}}ЗАПРОС
		;
		// Если ошибка в запросе, то выход из процедуры
		Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
			Возврат;
		КонецЕсли;
		ТЗ = СоздатьОбъект("ТаблицаЗначений");
		Запрос.Выгрузить(ТЗ,0,0);
		ТЗ.ВыбратьСтроку();
	КонецЕсли;
Показать
22. vitebsk13 15.08.14 11:28 Сейчас в теме
(19) pvase, нужно попробовать)
Вот код:
Если ВвестиСтроку(Наим,"Введите строку поиска",25,0)=1 Тогда
	    наим=нрег(СокрЛП(наим));	
	    СпрТов=СоздатьОбъект("Справочник.Номенклатура");
	    СпрТов.ВыбратьЭлементы();
		Пока СпрТов.ПолучитьЭлемент() = 1 Цикл	
			ТОвар=СПрТов.ТекущийЭлемент();
			Если Найти(Нрег(Товар.Наименование),Наим)<>0 Тогда
				Если Товар.ЭтоГРуппа()=0 тогда 
					Если Товар.ПометкаУдаления()=0 тогда 
				          тз.новаястрока();
                                          тз.товар=товар;
					КонецЕсли;
				КонецЕсли;
			КонецЕсли;
                КонецЦикла;
Конецесли;
	
Показать
21. pvase 401 15.08.14 11:20 Сейчас в теме
Но лучше конечно !С++ и 1sqlite и запрос будет работать гораздо шустрее.
23. Cooler 22 15.08.14 11:32 Сейчас в теме
ТОвар=СПрТов.ТекущийЭлемент();
Вот это нафига? Чтобы не слишком ускоряться? Ну, тогда дальше сами...
25. pvase 401 15.08.14 12:04 Сейчас в теме
(23) Cooler, Он все правильно делает. Спр.ТекущийЭлемент() - производит чтение записей по ID из таблицы справочника. Присвоение переменной - позволяет уйти от второго чтения тех же записей при обращении к справочнику по ТекущийЭлемент();. Т.е. каждое обращение к переменной Товар не будет производить поиск элемента в справочнике по ID, в отличие от ТекущийЭлемент(), который каждый раз производит чтение таблицы и поиск элемента по полю ID.
24. Улугбек 15.08.14 11:35 Сейчас в теме
Можно так:

	Если ВвестиСтроку(Наим,"Введите строку поиска",25,0)=1 Тогда
		наим=нрег(СокрЛП(наим));
		
		СпрТов = СоздатьОбъект("Справочник.Номенклатура");
		
		// если процедура в модуле формы списка
		Если ИерархическийСписок(,) = 1 Тогда
			СпрТов.ИспользоватьРодителя( ?(ЭтоГруппа() = 1, ТекущийЭлемент(),ТекущийЭлемент().Родитель) );
		КонецЕсли;
		
		СпрТов.ВыбратьЭлементы();
		Пока СпрТов.ПолучитьЭлемент() = 1 Цикл    
			
			Если СпрТов.ЭтоГруппа() + СпрТов.ПометкаУдаления() > 0 Тогда
				Продолжить;
			КонецЕсли;
			
			Если Найти(Нрег(Товар.Наименование), Наим) > 0 Тогда
				тз.новаястрока();
				тз.товар=товар;
			КонецЕсли;
		КонецЦикла;
	Конецесли;	
Показать
26. Cooler 22 15.08.14 12:15 Сейчас в теме
Присвоение переменной - позволяет уйти от второго чтения тех же записей при обращении к справочнику по ТекущийЭлемент()
Может, вы не заметили, но Спр.Наименование, Спр.ЭтоГруппа(), да и Спр.ПометкаУдаления() обходятся без второго чтения, оно осуществляется только при нахождении искомых элементов. В таких случаях да, мой код помедленнее отработает.

Зато в нем не будет вычисления Спр.ТекущийЭлемент() на каждом элементе, попадет он потом в выборку или нет.

Впрочем, без замера производительности это все уже пустые разговоры, так что я закругляюсь.

Вот ваш запрос может дать существенно другие результаты за счет переноса процесса перебора из интерпретируемого кода внутрь движка 1С, а будут они лучше или хуже - ХЗ.

P.S. Интересно, автор учитывает, что при втором и последующих запусках обработки она может отрабатывать быстрее в разы за счет того, что справочник попал в кэш?
27. pvase 401 15.08.14 12:26 Сейчас в теме
Cooler Согласен конкретно в этом примере это лишнее. Лучше тогда:
Если ВвестиСтроку(Наим,"Введите строку поиска",25,0)=1 Тогда
	наим=нрег(СокрЛП(наим));    
	СпрТов=СоздатьОбъект("Справочник.Номенклатура");
	СпрТов.ВыбратьЭлементы();
	Пока СпрТов.ПолучитьЭлемент() = 1 Цикл    
		Если Найти(Нрег(СпрТов.Наименование),Наим)<>0 Тогда
			Если (СпрТов.ЭтоГРуппа()=0) И (СпрТов.ПометкаУдаления()=0) тогда 
				тз.новаястрока();
				тз.товар=СПрТов.ТекущийЭлемент();;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
Конецесли;
Показать

Но надо проверить какой алгоритм будет быстрее через перебор или через запрос 1С (если не хочется делать через прямой запрос в базе).
28. Cooler 22 15.08.14 12:55 Сейчас в теме
(27)
Если (СпрТов.ЭтоГРуппа()=0) И (СпрТов.ПометкаУдаления()=0) тогда
Насчет вот этого у меня есть сомнения. С одной стороны, для максимального ускорения я против объединения этих условий: если ЭтоГруппа()=1, то проверять на удаление уже излишне, а в таком варианте эта проверка будет выполняться.

С другой стороны, если как в (22) вложить вторую проверку в первую, то это приводит к появлению дополнительных Если ... КонецЕсли.

И неизвестно, что движок 1С отработает быстрее.

И, уж если занудствовать по поводу оптимизации до упора, то надо делать:

//       Если Найти(Нрег(СпрТов.Наименование),Наим)<>0 Тогда
       Если Найти(Нрег(СпрТов.Наименование),Наим)>0 Тогда


Найти() не может дать отрицательного результата, и незачем заставлять прогу делать пустяковую, но тоже лишнюю проверку.
29. _Z1 38 15.08.14 14:30 Сейчас в теме
(28) 1c77 выполняет все условия в выражении И
т.е если вы напишите

Если (0 = 1) И (Функция1() = 1) И (Функция2() = 1) И (Функция3() = 1) Тогда

1с все равно вычислит все три функции.
так что вложеные Если лучше

еще есть ? но с ним текст совсем становиться нечитаемым
31. ivsher 15.08.14 14:39 Сейчас в теме
(29) _Z1, Так что бы правильно рассчитать результат выражения ((Функция1()=Чему-то) И (Функция2()=Чему-то) И (Функция3()=Чему-то))
какраз таки и необходимо получить значение всех 3 функций. Вот если бы И поменять на ИЛИ тогда да, после первого же верного условия Функция()=Чему-то, можно прекращать дальнейший расчет функций.
32. tdr1225 37 15.08.14 14:50 Сейчас в теме
(31) подобную оптимизацию делают умные компиляторы
очень сомневаюсь, что интерпретер 1С настолько умён
33. tdr1225 37 15.08.14 14:53 Сейчас в теме
+32
это значит, что в случае
икс=0;
Если икс+0=0 Тогда ...

он будет вполнять и присваивание, и сложение, и сравнение
34. MIracloid2000 15.08.14 15:14 Сейчас в теме
(33) tdr1225, а есть НАСТОЛЬКО умные компиляторы, что икс+0=0 будут вам ругаться, что код не оптимизированный?
кх-м, не слышал о таких... даже для компилятора, что икс+0=0, что икс+7=7 - выполняется одинаково

а автору топика: бредовая реализация бредовой задачи, как я понимаю, кто-то умный захотел, чтобы набрав "кра", получить все ТМЦ, в которых есть эта подстрока. Так подскажу решение, пользователям, которые вносят новые название ТМЦ отбить руки, а потом за КАЖДУЮ ошибку не так введеное название штрафовать, тогда и проблема решиться в течении срока "до следующей зарплаты"
35. tdr1225 37 15.08.14 15:29 Сейчас в теме
а есть НАСТОЛЬКО умные компиляторы, что икс+0=0 будут вам ругаться, что код не оптимизированный

неточно цитируешь. Перед этим сравнением выполняется икс=0;
И именно умный компилятор увидит, что условие верно _всегда_, и в скомпилированном коде сравнение будет отсутствовать.
Компилятор не будет ругаться на неоптимальный код, он просто его соптимизирует.
36. pvase 401 15.08.14 15:44 Сейчас в теме
Поскольку 7.7. не является компилятором - то ему оптимизация не грозит :).
38. vcv 89 15.08.14 21:43 Сейчас в теме
(36) Поскольку при выполнении модуля 1с происходит не интерпретация, а трансляция исходного кода в, по всей видимости, байт-код, и только потом исполнение, 1с подходит под определение компилятора. Если бы 1с была чистым интерпретатором, не могло бы существовать КЗК для 7.7 и поставки без исходных кодов для 8.х
37. tdr1225 37 15.08.14 15:59 Сейчас в теме
и я об этом же. см посты 31-32
39. vcv 89 15.08.14 21:47 Сейчас в теме
Четкой границы между компилятором и интерпретатором нет. В том же Java происходит сначала компиляция исходного кода в байт-код, потом интерпретация программы на байт-коде.
44. CheBurator 3119 20.08.14 02:29 Сейчас в теме
для дбф базы, заготовка
.
//==========================================================­============
Функция ПоискПоПодстрокеSQlite()

//если здесь - значит на периферии
//примем административное правило, на периферии - ДБФ базы
Попытка
глВКлайт.Открыть(":memory:");
Запрос = глВКлайт.НовыйЗапрос();
Запрос.ВыполнитьЗапрос("create virtual table Товары using dbeng(Справочник.Номенклатура)");
Исключение
//тОшибка = "инфо: проблема при выполнении запроса инициализации вирт.таблицы, при случае - сообщите программисту: "+ОписаниеОшибки();
//Сообщить(тОшибка);
Возврат 0;
КонецПопытки;

//ОТЛАДКА
//Сообщить("работаем SQLite");

РазбиваемаяСтрока = ПодСтрокаПоиска;
РазбиваемаяСтрока = СтрЗаменить(РазбиваемаяСтрока,"'","");
РазбиваемаяСтрока = СтрЗаменить(РазбиваемаяСтрока,"""","");
РазбиваемаяСтрока = СтрЗаменить(СокрЛП(РазбиваемаяСтрока)," ",РазделительСтрок);

ТекстЗапроса = "SELECT
|Товары.id [Элемент :Справочник.Номенклатура]
|FROM Товары
|WHERE Товары.isfolder=2 and Товары.ismark <> '*'";

Для Индекс = 1 По СтрКоличествоСтрок(РазбиваемаяСтрока)
Цикл Слово = Врег(СтрПолучитьСтроку(РазбиваемаяСтрока,Индекс));

Если ПустоеЗначение(Слово)=1
Тогда Продолжить;
КонецЕсли;
ТекстЗапроса = ТекстЗапроса+"
|AND UPPER(Товары.descr) LIKE '%"+Слово+"%'";
КонецЦикла;

//ТекстЗапроса = ТекстЗапроса+"
//|ORDER BY Элемент.descr";

ТЗраб = СоздатьОбъект("ТаблицаЗначений");
Попытка
_время1 = _GetPerformanceCounter();
Запрос.ВыполнитьЗапрос(ТекстЗапроса).Выгрузить(ТЗраб);
//Сообщить((_GetPerformanceCounter()-_время1)/1000);
//ч=1/0;
Исключение
глВКлайт.Закрыть();
тОшибка = "ошибка при выполнении запроса, сообщите программисту: "+ОписаниеОшибки();
Сообщить(тОшибка);
Возврат 0;
КонецПопытки;
глВКлайт.Закрыть();
Оставьте свое сообщение

Для получения уведомлений об ответах подключите телеграм бот:
Инфостарт бот