Замер производительности 1С 8.3: как пользоваться

Дата: 06.10.2026 16:54:16
Замер производительности
Производительность 1С
Отладка 1С
Оптимизация запросов
Программирование 1С
Комментарий:
0
Платформа:
8.3
Конфигурация:
Любая
Формы:
Любые

Замер производительности 1С — режим отладчика, который показывает, какие строки кода выполнялись во время операции, сколько раз и сколько времени заняли. Он нужен, когда тормозит конкретное действие: проведение документа, заполнение табличной части, кнопка в обработке, открытие формы. Ниже — как пользоваться замером производительности в конфигураторе 8.3, как дойти до медленной строки, в том числе в серверном коде, и пример обработки, которую после замера ускорили в разы.

Замеры производительности 1С 8.3 работают в типовых и самописных конфигурациях, на управляемых и обычных формах и правки кода не требуют.

Как сделать замер производительности 1С 8.3 в отладчике

Замер включается только во время отладки: запустите 1С:Предприятие из конфигуратора (F5) или подключитесь к клиенту, запущенному в режиме отладки, через «Отладка → Подключение».

  1. Возьмите копию рабочей базы с реальным объёмом данных: на демо-базе медленная строка не проявится.
  2. Подведите операцию к началу: откройте документ или форму, заполните отборы. Точки останова на время замера отключите.
  3. В конфигураторе выберите «Отладка → Замер производительности».
  4. Выполните в 1С:Предприятии только проверяемое действие, например нажмите «Провести».
  5. Снова выберите «Отладка → Замер производительности»: замер остановится и откроется окно с результатами.

Паузы между действиями в результат не входят: общее время замера — сумма промежутков, когда выполнялся код конфигурации. Зато входит всё выполненное: открытие форм, обработчики ожидания, соседние команды, поэтому замер включают прямо перед операцией. Если тормозит открытие формы, включите замер до открытия — попадут ПриСозданииНаСервере и ПриОткрытии.

Входите под пользователем, у которого тормозит: ограничения доступа на уровне записей усложняют запросы, а для полных прав не применяются. Подробнее — в статье о правах доступа 1С.

Как читать результат: колонки окна замера

Результат — таблица со ссылками на строки модулей. Колонки в конфигураторе 8.3:

КолонкаЧто показывает
Модуль, Номер строки, СтрокаГде находится строка и её текст
Кол.Сколько раз строка выполнилась за время замера
Врем.Суммарное время выполнения строки, в секундах
%(Врем.)Доля от общего времени замера; за 100% принимается время выполнения кода на клиенте
Клиент, СерверПиктограмма показывает, где выполнялась строка
Обр. серверСтрока привела к вызову сервера — напрямую или через вызванную клиентскую процедуру

Флажок «Для вызовов процедур и функций включать время выполнения» по умолчанию установлен: время строки с вызовом процедуры включает всё, что выполнялось внутри неё. Если флажок снять, останется «чистое время» — только операторы самой строки, а колонки времени сменят названия. Состояние флажка запоминается между сеансами.

Щелчок по заголовку колонки сортирует таблицу, двойной щелчок по строке открывает модуль. Результат виден и в редакторе модуля: у строк появляется колонка с числом выполнений и долей времени, двойной щелчок по ней возвращает к строке в окне замера.

Как найти узкое место до строки кода

  1. Отсортируйте по «%(Врем.)» по убыванию. Наверху окажется цепочка вызовов: обработчик команды с долей около 100%, вызванная из него процедура, следующая за ней. Спускайтесь по цепочке, пока доля не разделится между несколькими строками.
  2. Снимите флажок полного времени. Теперь наверху строки, которые тратят время сами: Запрос.Выполнить(), Записать(), обращение к реквизиту через точку.
  3. Поделите «Врем.» на «Кол.» — получится время одного выполнения. Миллисекунды при тысячах выполнений — проблема в цикле, секунды при одном выполнении — в самой операции.
Что видно в замереПричинаЧто делать
Строка с запросом, «Кол.» равно числу строк таблицыЗапрос в циклеОдин запрос на весь набор данных
СтрокаТоваров.Номенклатура.Артикул с тысячами выполненийЧтение объекта по ссылке на каждой итерацииПолучить реквизит в том же запросе
Клиентская строка в цикле с отметкой «Обр. сервер»Вызов сервера на каждую строкуСобрать данные и вызвать сервер один раз
«Кол.» = 1, но большая доля времениТяжёлый запрос или записьРазбирать текст запроса, индексы, блокировки

В последнем случае разбирайте условия в параметрах виртуальных таблиц и соединения — см. статью об оптимизации запросов. Медленную строку в типовом модуле исправляйте расширением, а не снятием с поддержки.

Пример: обработку остатков ускорили в разы

Обработка готовит таблицу для выгрузки на сайт: товар, артикул, единица хранения и остаток на складе. Чем больше ассортимент, тем дольше заполнение. Код до замера — модуль объекта обработки, объекты УТ 11 и КА 2:

// Модуль объекта обработки «Остатки для выгрузки» (объекты УТ 11 и КА 2).
// Было: так код выглядел до замера. Товары - таблица значений с колонками
// Номенклатура, Артикул, ЕдиницаИзмерения, Остаток; Склад - СправочникСсылка.Склады.
Процедура ЗаполнитьОстаткиПострочно(Товары, Склад) Экспорт

	Для Каждого СтрокаТоваров Из Товары Цикл

		// Реквизиты «через точку»: для каждого товара - чтение объекта из базы
		СтрокаТоваров.Артикул = СтрокаТоваров.Номенклатура.Артикул;
		СтрокаТоваров.ЕдиницаИзмерения = СтрокаТоваров.Номенклатура.ЕдиницаИзмерения;

		// Запрос в цикле: обращений к СУБД столько же, сколько строк в таблице
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ЕСТЬNULL(СУММА(Остатки.ВНаличииОстаток), 0) КАК Остаток
		|ИЗ
		|	РегистрНакопления.ТоварыНаСкладах.Остатки(
		|			,
		|			Номенклатура = &Номенклатура
		|				И Склад = &Склад) КАК Остатки";
		Запрос.УстановитьПараметр("Номенклатура", СтрокаТоваров.Номенклатура);
		Запрос.УстановитьПараметр("Склад", Склад);

		Выборка = Запрос.Выполнить().Выбрать();
		СтрокаТоваров.Остаток = ?(Выборка.Следующий(), Выборка.Остаток, 0);

	КонецЦикла;

КонецПроцедуры

В замере такой код даёт узнаваемую картину: почти всё время делят строки внутри цикла — Запрос.Выполнить() и чтение артикула через точку, а «Кол.» у них равно числу товаров. Одно выполнение занимает миллисекунды, но каждое — отдельное обращение к базе, и их тысячи.

Исправленный вариант — один пакетный запрос на всю таблицу:

// Модуль объекта обработки (продолжение). Стало: один пакетный запрос
// на всю таблицу вместо запроса и чтения объекта на каждую строку.
// Параметры те же, что у ЗаполнитьОстаткиПострочно.
Процедура ЗаполнитьОстаткиОднимЗапросом(Товары, Склад) Экспорт

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Остатки.Номенклатура КАК Номенклатура,
	|	СУММА(Остатки.ВНаличииОстаток) КАК Остаток
	|ПОМЕСТИТЬ ВТ_Остатки
	|ИЗ
	|	РегистрНакопления.ТоварыНаСкладах.Остатки(
	|			,
	|			Склад = &Склад
	|				И Номенклатура В (&СписокНоменклатуры)) КАК Остатки
	|
	|СГРУППИРОВАТЬ ПО
	|	Остатки.Номенклатура
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Номенклатура
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Номенклатура.Ссылка КАК Номенклатура,
	|	Номенклатура.Артикул КАК Артикул,
	|	Номенклатура.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
	|	ЕСТЬNULL(ВТ_Остатки.Остаток, 0) КАК Остаток
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Остатки КАК ВТ_Остатки
	|		ПО Номенклатура.Ссылка = ВТ_Остатки.Номенклатура
	|ГДЕ
	|	Номенклатура.Ссылка В (&СписокНоменклатуры)";
	Запрос.УстановитьПараметр("Склад", Склад);
	Запрос.УстановитьПараметр("СписокНоменклатуры", Товары.ВыгрузитьКолонку("Номенклатура"));

	// Результат - в соответствие: строка находится по ссылке без перебора
	Данные = Новый Соответствие;
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Данные.Вставить(Выборка.Номенклатура, Новый Структура("Артикул, ЕдиницаИзмерения, Остаток",
			Выборка.Артикул, Выборка.ЕдиницаИзмерения, Выборка.Остаток));
	КонецЦикла;

	Для Каждого СтрокаТоваров Из Товары Цикл
		ДанныеТовара = Данные.Получить(СтрокаТоваров.Номенклатура);
		Если ДанныеТовара <> Неопределено Тогда
			ЗаполнитьЗначенияСвойств(СтрокаТоваров, ДанныеТовара);
		КонецЕсли;
	КонецЦикла;

КонецПроцедуры
  • Условие Номенклатура В (&СписокНоменклатуры) стоит в параметрах виртуальной таблицы Остатки: остатки считаются только по нужным товарам, а не по всему складу.
  • СГРУППИРОВАТЬ ПО гарантирует одну строку на товар, даже если остаток разнесён по характеристикам, сериям и назначениям: такие измерения у регистра ТоварыНаСкладах есть.
  • Артикул и единица берутся соединением со справочником в том же запросе, объекты по ссылкам не читаются.
  • Соответствие находит данные товара по ссылке без перебора; повторяющиеся товары заполнятся одинаково.

Повторный замер: вместо тысяч выполнений — одна строка Запрос.Выполнить() с «Кол.» = 1. Для десятков тысяч позиций список лучше передать временной таблицей (см. статью о таблице значений в запросе), другие приёмы — в статье о запросе в цикле. Запрос удобно проверить отдельно в консоли запросов.

Ускорение меряйте вне отладчика: под ним код работает медленнее. Стенд прогоняет оба варианта на одних данных:

// Модуль объекта обработки (продолжение): стенд «до/после» на одних данных.
// Запускать вне отладчика, из серверной процедуры формы обработки:
// Сообщить(РеквизитФормыВЗначение("Объект").СравнитьВарианты(Объект.Склад, 2000));
Функция СравнитьВарианты(Склад, КоличествоТоваров = 1000) Экспорт

	Товары = НоваяТаблицаТоваров(КоличествоТоваров);

	// Прогрев: первый проход по данным медленнее последующих, его не считаем
	ЗаполнитьОстаткиОднимЗапросом(Товары, Склад);

	Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
	ЗаполнитьОстаткиПострочно(Товары, Склад);
	Построчно = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало;

	Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();
	ЗаполнитьОстаткиОднимЗапросом(Товары, Склад);
	ОднимЗапросом = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало;

	Возврат СтрШаблон("Строк: %1. Построчно: %2 мс, одним запросом: %3 мс, быстрее в %4 раза",
		Формат(Товары.Количество(), "ЧН=0; ЧГ=0"),
		Формат(Построчно, "ЧН=0; ЧГ=0"),
		Формат(ОднимЗапросом, "ЧН=0; ЧГ=0"),
		Формат(Построчно / Макс(ОднимЗапросом, 1), "ЧДЦ=1; ЧН=0"));

КонецФункции

// Тестовые данные: первые N элементов номенклатуры без групп и помеченных на удаление.
Функция НоваяТаблицаТоваров(Количество)

	Товары = Новый ТаблицаЗначений;
	Товары.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
	Товары.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка"));
	Товары.Колонки.Добавить("ЕдиницаИзмерения");
	Товары.Колонки.Добавить("Остаток", Новый ОписаниеТипов("Число"));

	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	Номенклатура.Ссылка КАК Номенклатура
	|ИЗ
	|	Справочник.Номенклатура КАК Номенклатура
	|ГДЕ
	|	НЕ Номенклатура.ЭтоГруппа
	|	И НЕ Номенклатура.ПометкаУдаления";
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "ПЕРВЫЕ 1000",
		"ПЕРВЫЕ " + Формат(Макс(Количество, 1), "ЧГ=0"));

	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Товары.Добавить().Номенклатура = Выборка.Номенклатура;
	КонецЦикла;

	Возврат Товары;

КонецФункции

Первый проход прогревочный: по холодным данным медленнее любой вариант. Итоговое число и есть ответ «в разы или нет» для вашей базы и объёма данных.

Замер серверного кода

В файловой базе серверный код выполняется в процессе клиента и при запуске из конфигуратора попадает в замер без настроек. В клиент-серверной базе серверные строки появятся, только если сервер 1С работает в режиме отладки (агент кластера запущен с ключом -debug) и серверный предмет отладки подключён в «Отладка → Подключение» — см. статью об отладке на сервере 1С.

Без серверной отладки клиентская строка с вызовом сервера всё равно покажет полное время вызова и отметку в «Обр. сервер», но содержимое вызова не раскроется. Флажки «Клиент» и «Сервер» в окне результатов отбирают строки по месту выполнения; если они неактивны, серверный предмет не отлаживается и сервер в замер не попал.

Сортировка по «Обр. сервер» собирает вместе строки, которые вызывали сервер. Строка из клиентского цикла среди них означает вызов на каждой итерации со своими накладными расходами. Та же задача в форме обработки:

// Модуль формы обработки. На форме: табличная часть Объект.Товары
// (Номенклатура, Остаток), реквизит Объект.Склад и две команды.

// Было: вызов сервера на каждую строку. В замере у строки с вызовом
// отметка в колонке «Обр. сервер», а «Кол.» равно числу строк.
&НаКлиенте
Процедура ОбновитьОстаткиПострочно(Команда)
	Для Каждого СтрокаТоваров Из Объект.Товары Цикл
		СтрокаТоваров.Остаток = ОстатокТовара(СтрокаТоваров.Номенклатура, Объект.Склад);
	КонецЦикла;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ОстатокТовара(Номенклатура, Склад)
	Список = Новый Массив;
	Список.Добавить(Номенклатура);
	Остаток = ОстаткиТоваров(Список, Склад).Получить(Номенклатура);
	Возврат ?(Остаток = Неопределено, 0, Остаток);
КонецФункции

// Стало: один вызов сервера на всю таблицу.
&НаКлиенте
Процедура ОбновитьОстатки(Команда)
	Список = Новый Массив;
	Для Каждого СтрокаТоваров Из Объект.Товары Цикл
		Список.Добавить(СтрокаТоваров.Номенклатура);
	КонецЦикла;
	Остатки = ОстаткиТоваров(Список, Объект.Склад);
	Для Каждого СтрокаТоваров Из Объект.Товары Цикл
		Остаток = Остатки.Получить(СтрокаТоваров.Номенклатура);
		СтрокаТоваров.Остаток = ?(Остаток = Неопределено, 0, Остаток);
	КонецЦикла;
КонецПроцедуры

// Остатки по списку товаров: Соответствие «товар - остаток».
// Знач: параметры не передаются обратно на клиент после вызова.
&НаСервереБезКонтекста
Функция ОстаткиТоваров(Знач Список, Знач Склад)
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Остатки.Номенклатура КАК Номенклатура,
	|	СУММА(Остатки.ВНаличииОстаток) КАК Остаток
	|ИЗ
	|	РегистрНакопления.ТоварыНаСкладах.Остатки(
	|			,
	|			Склад = &Склад
	|				И Номенклатура В (&Список)) КАК Остатки
	|
	|СГРУППИРОВАТЬ ПО
	|	Остатки.Номенклатура";
	Запрос.УстановитьПараметр("Склад", Склад);
	Запрос.УстановитьПараметр("Список", Список);

	Остатки = Новый Соответствие;
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Остатки.Вставить(Выборка.Номенклатура, Выборка.Остаток);
	КонецЦикла;
	Возврат Остатки;
КонецФункции
  • В первом варианте у строки с ОстатокТовара(...) «Кол.» равно числу строк и есть отметка в «Обр. сервер».
  • Во втором клиент один раз передаёт массив ссылок и получает Соответствие — оба типа передаются между клиентом и сервером.
  • Знач у параметров серверной функции избавляет от обратной передачи параметров на клиент.

Код фонового или регламентного задания выполняется в отдельном сеансе. Проще всего замерить его, вызвав тот же метод из обработки в текущем сеансе, — приём есть в статье об отладке 1С.

Показатели производительности: вызовы сервера без конфигуратора

Число серверных вызовов и объём данных видно и без отладчика. В 1С:Предприятии установите флажок «Отображать показатели производительности» в «Сервис → Параметры» или запустите клиент с ключом /DisplayPerformance. В правом нижнем углу появится кнопка, которая открывает окно со счётчиками «Текущие» и «Накопленные»; из контекстного меню этого окна открывается «История вызовов сервера» с колонками «Начало», «Вызовов», «Время», «Отправлено», «Принято». После правки команда обновления остатков должна давать один вызов сервера, а не вызов на строку.

Типичные ошибки

  • Замер на пустой базе. На сотне записей любой код быстрый.
  • В замере нет серверных строк. Сервер запущен без ключа -debug или не подключён серверный предмет отладки; признак — неактивные флажки «Клиент» и «Сервер».
  • Изучают строку со 100%. Это обработчик команды, в нём вся операция: спускайтесь по цепочке или снимите флажок полного времени.
  • Секунды под отладчиком сравнивают с рабочей базой. Под отладкой смотрите на доли и «Кол.», а ускорение меряйте стендом.
  • Замер шёл несколько минут. Лишние действия и обработчики ожидания заслонили нужную строку.
  • «Поле объекта не обнаружено (Товары)». Код формы вставили в обработку без табличной части Товары или реквизита Склад.

Частые вопросы

Почему сумма процентов больше 100?

С флажком полного времени время вложенной процедуры учитывается и у строки с вызовом, и у строк внутри неё. Чтобы увидеть вклад отдельных операторов, снимите флажок.

Можно ли сохранить замер и сравнить «до» и «после»?

Да, результат сохраняется в файл. Если открыть два замера, в редакторе модуля будут колонки обоих, а подсказка над колонкой покажет, из какого файла данные.

Как использовать замер производительности, если отладку на рабочем сервере включать нельзя?

Повторите операцию на копии: файловой, где серверный код попадёт в замер без настройки кластера, или на тестовом сервере с отладкой. На файловой копии циклы и лишние вызовы видны так же, но время отдельных запросов может отличаться. Плавающие тормоза ловят технологическим журналом — см. настройку техжурнала.

Чем замер отладчика отличается от «Оценки производительности» БСП?

Подсистема БСП меряет время ключевых операций у пользователей рабочей базы и пишет его в регистр «Замеры времени» для оценки по APDEX. В УТ 11.4 она включается флажком «Оценка производительности» в «Администрирование → Обслуживание», а в коде замер ставят парой ОценкаПроизводительности.НачатьЗамерВремени() и ОценкаПроизводительности.ЗакончитьЗамерВремени(КлючеваяОперация, ВремяНачала). Подсистема отвечает на вопрос «что и у кого тормозит», замер отладчика — «на какой строке».

Есть ли замер производительности в EDT?

Есть: панель «Замер производительности» открывается после завершения замера. Колонки там называются «Кол-во вызовов», «Полное время (с)» и «Чистое время (с)», результаты хранятся в рабочей области.

Когда проще заказать

Замер показывает, где тормозит, но переписывать узкое место в проведении, обмене или типовом механизме нужно аккуратно, обычно через расширение. Если на это нет времени, приложите файл замера к задаче: профильная услуга — «1С тормозит», разместить задачу можно бесплатно, ответ с оценкой — в течение рабочего дня.

По теме

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.


1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.