Какой цикл в 1С работает быстрее: Для каждого, Для или Пока?

Программист 1С v8.3 (Управляемые формы)
← На главную

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

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

Общий рейтинг производительности для коллекций в памяти

Если мы говорим о переборе коллекций, которые уже полностью загружены в оперативную память (например, Массив, СписокЗначений или ТаблицаЗначений), то существует общее правило производительности.

От самого быстрого к самому медленному циклы располагаются так:

  1. Для Каждого ... Из ... Цикл
  2. Для ... По ... Цикл
  3. Пока ... Цикл

Почему такая разница? Причина кроется во внутренней реализации на уровне платформы 1С.

Цикл Для Каждого работает на более низком уровне. Он напрямую использует внутренний механизм обхода коллекции (итератор), что делает его максимально эффективным. Платформа просто последовательно получает следующий элемент, не выполняя лишних операций.

В то же время, цикл Для ... По ... Цикл, использующий индекс, на каждой итерации выполняет больше действий на уровне встроенного языка: получает элемент коллекции по его индексу (МойМассив[i]), что является дополнительной операцией с накладными расходами.

Посмотрим на пример:


// Более быстрый и предпочтительный вариант для перебора массива
Для Каждого Элемент Из МояКоллекция Цикл
    // Действия с элементом
КонецЦикла;

// Более медленный вариант с явным получением по индексу
Для i = 0 По МояКоллекция.ВГраница() Цикл
    Элемент = МояКоллекция[i];
    // Действия с элементом
КонецЦикла;

Вывод: для обхода уже готовых коллекций в памяти всегда отдавайте предпочтение циклу Для Каждого. Он не только быстрее, но и делает код более читаемым.

Ключевое отличие: перебор результатов запроса

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

Подход 1: Выгрузить() и затем цикл "Для Каждого"

Этот подход кажется логичным: сначала получаем все данные в таблицу значений, а потом обрабатываем ее самым быстрым циклом Для Каждого. Разберем по шагам:

  1. Выполняется запрос к базе данных.
  2. Метод Выгрузить() создает в оперативной памяти полную копию результата запроса в виде объекта ТаблицаЗначений. Это самый затратный этап. Если результат запроса содержит сотни тысяч или миллионы строк, это может занять много времени и потребовать значительный объем оперативной памяти.
  3. Быстрый цикл Для Каждого перебирает уже готовую таблицу в памяти.

Посмотрим на пример кода:


Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Номенклатура.Ссылка ИЗ Справочник.Номенклатура КАК Номенклатура";

// Шаг 1: Полная выгрузка результата в память. Может быть очень медленно и ресурсоемко.
ТаблицаРезультата = Запрос.Выполнить().Выгрузить(); 

// Шаг 2: Быстрый цикл по уже готовой таблице в памяти.
Для Каждого Строка Из ТаблицаРезультата Цикл
    // Обработка строки
    Сообщить(Строка.Ссылка);
КонецЦикла;

Несмотря на то, что сам цикл работает быстро, общая производительность связки Выгрузить() + Для Каждого часто оказывается низкой из-за "тяжелой" операции выгрузки данных.

Подход 2: Выбрать() и цикл "Пока Выборка.Следующий()"

Этот подход является более профессиональным и производительным для обработки результатов запросов. Разберем его:

  1. Выполняется запрос, но результат не выгружается в память полностью. Вместо этого создается специальный объект ВыборкаИзРезультатаЗапроса.
  2. Цикл Пока Выборка.Следующий() итерационно обходит результат. На каждом шаге платформа подгружает данные порциями, не создавая полную копию в памяти.

Посмотрим на пример:


Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Номенклатура.Ссылка ИЗ Справочник.Номенклатура КАК Номенклатура";

РезультатЗапроса = Запрос.Выполнить();

// Создается объект "Выборка", данные из базы будут подгружаться по мере необходимости
Выборка = РезультатЗапроса.Выбрать();

// Сам цикл "Пока" медленнее, чем "Для каждого", 
// но за счет отсутствия этапа полной выгрузки общий выигрыш в скорости и памяти огромен.
Пока Выборка.Следующий() Цикл
    // Обработка текущей записи выборки
    Сообщить(Выборка.Ссылка);
КонецЦикла;

Сравнение подходов: Хотя сам по себе цикл Пока медленнее, чем Для Каждого, связка Выбрать() + Пока Выборка.Следующий() почти всегда выигрывает при работе с запросами. Она потребляет на порядки меньше оперативной памяти и позволяет начать обработку данных немедленно, не дожидаясь полной выгрузки.

Современный подход и практические рекомендации

Борьба за миллисекунды при выборе цикла — это чаще всего преждевременная оптимизация. Гораздо больший эффект дает правильный подход к алгоритмам и работе с данными.

  1. Контекст важнее всего. Основной источник "тормозов" — это неэффективные запросы или лишние обращения к серверу внутри цикла. Оптимизируйте в первую очередь сам запрос (например, через соединения), чтобы получить максимум нужных данных за один раз, а не выполнять дополнительные запросы на каждой итерации.
  2. Читаемость кода — в приоритете. Если у вас цикл по 100 элементам, разница в скорости будет незаметна. Выбирайте тот цикл, который делает код более понятным и логичным для данной задачи.
  3. Проверяйте на реальных данных. Производительность платформы 1С меняется от релиза к релизу. Единственный надежный способ узнать, что быстрее в вашем конкретном случае и на ваших объемах данных — это использовать встроенный инструмент "Замер производительности" в Конфигураторе.

Итог: какой цикл выбрать?

Подведем итоги и сформулируем простые правила:

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

← На главную