HelpF.pro

Циклы в языке 1С, примеры и тест - какой цикл быстрее?

Циклы применяются для выполнения каких либо повторяющихся действий, возможные варианты перебора в цикле:

Перебираем строки с помощью цикла Для каждого

Код 1C v 8.3
 Для каждого ТекСтрока Из КоллекцияСтрок Цикл
// код обработки
КонецЦикла;

Перебираем строки с помощью цикла Пока

Код 1C v 8.3
 Пока л < КолСтрок Цикл
// код обработки, например л=л+1;
КонецЦикла;

Перебираем строки с помощью цикла Для

Код 1C v 8.3
 Для л=0 по КолСтрок Цикл
// код обработки, например л=л+1;
КонецЦикла

Еще вариант, но советую его использовать только в без выходных ситуациях, Если

Код 1C v 8.3
 ~НачалоЦикла:
Если л <> КоличествоИтераций Тогда 
// код обработки, например л=л+1;
Перейти ~НачалоЦикла;
КонецЕсли;

Примеры циклов

Код 1C v 8.2 УП
 &НаКлиенте
Процедура ВыполнитьКод(Команда)
 
    /// Как организовать цикл в 1с 8.3, 8.2
 
    // Для Цикл
    Для Счетчик = 1 По 5 Цикл
        Сообщить(Счетчик); // 1 2 3 4 5
    КонецЦикла;
 
    // Для Каждого Цикл
 
    Дни = Новый Массив();
    Дни.Добавить("Понедельник");
    Дни.Добавить("Вторник");
    Дни.Добавить("Среда");
 
    Для Каждого Элемент Из Дни Цикл
        Сообщить(Элемент); // Понедельник Вторник Среда
    КонецЦикла;
 
    // Пока Цикл
    Счетчик = 0;
    Пока Счетчик < Дни.Количество() Цикл        
        Сообщить(Дни[Счетчик]); // Понедельник Вторник Среда
        Счетчик = Счетчик + 1;     
    КонецЦикла;     
 
    /// Как организовать обратный цикл в 1с 8.3, 8.2     
 
    Счетчик = Дни.Количество() - 1;     
    Пока Счетчик >= 0 Цикл
        Сообщить(Дни[Счетчик]); // Среда Вторник Понедельник
        Счетчик = Счетчик - 1;
    КонецЦикла;
 
    /// Как прервать цикл в 1с 8.3, 8.2
 
    Для Счетчик = 1 По 5 Цикл
        Если Счетчик > 2 Тогда
            Прервать;
        КонецЕсли;
        Сообщить(Счетчик); // 1 2
    КонецЦикла;
 
    /// Как принудительно продолжить цикл в 1с 8.3, 8.2
 
    Для Счетчик = 1 По 5 Цикл
        Если Счетчик <> 3 Тогда
            Продолжить;
        КонецЕсли;
 
        Сообщить(Счетчик); // 3
    КонецЦикла;   
 
КонецПроцедуры

А какой цикл работает быстрее?

Итак, я нашел пять способов, как можно организовать цикл средствами 1С.

Первый вид цикла, назовем его условно «Для По» выглядит так:

Код 1C v 8.х
 Для н = 0 по КоличествоИтераций Цикл
КакиеТоДействия();
КонецЦикла;

Второй вид «Для Каждого»:

Код 1C v 8.х
 Для Каждого ЭлементКоллекции из Коллекция Цикл
КакиеТоДействия();
КонецЦикла;

Третий «Пока»:

Код 1C v 8.х
 Пока н <> КоличествоИтераций Цикл
КакиеТоДействия();
н = н + 1;
КонецЦикла;

Далее вспомнил ассемблерную молодость & цикл «Если»:

Код 1C v 8.х
 ~НачалоЦикла:
Если н <> КоличествоИтераций Тогда
КакиеТоДействия();
н = н + 1;
Перейти ~НачалоЦикла;
КонецЕсли;

Ну и напоследок «Рекурсия»

Код 1C v 8.х
 Процедура РекурсивныйЦикл(н, КоличествоИтераций)
КакиеТоДействия();
Если н <> КоличествоИтераций Тогда
РекурсивныйЦикл(н+1, КоличествоИтераций);
КонецЕсли;
КонецПроцедуры

Естественно, что относить рекурсию к циклам не совсем корректно, но тем ни менее с её помощью можно добиться похожих результатов. Сразу оговорюсь, что в дальнейшем тестировании рекурсия не участвовала. Во первых все тесты проводились при 1 000 000 итераций, а рекурсия выпадает уже при 2 000. Во вторых скорость рекурсии в десятки раз меньше, чем скорость остальных циклов.

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

Ну что ж, поехали. В качестве тела цикла использовалось чтение из заранее заполненного массива.

Код 1C v 8.х
 ПриемникТестовогоЗначения = ТестовыйМассив.Получить(н);   

или, при использовании цикла «Для Каждого»

Код 1C v 8.х
 ПриемникТестовогоЗначения = Элем;   

Тестирование проводилось на платформе 8.3.5.1231 для трех видов интерфейса (Обычное приложение, Управляемое приложение и Такси).

Результаты для 8.3.5.1231
ИнтерфейсДляПоДляКаждогоПокаЕсли
Обычное приложение5734,24680,47235,47263,0
Управляемое приложение5962,44882,67497,47553,6
Такси5937,24854,67500,87513,0

Числа это время в миллисекундах полученное с помощью функции ТекущаяУниверсальнаяДатаВМиллисекундах(), которую я вызывал до цикла и после его завершения. Числа дробные, потому что я использовал среднее арифметическое пяти замеров. Почему я не использовал Замер производительности? У меня не было цели замерить скорость каждой строчки кода, только скорость циклов с одинаковым результатом работы.

Казалось бы и все, но & тестировать так тестировать!

Результаты для 8.2.19.106
ИнтерфейсДляПоДляКаждогоПокаЕсли
Обычное приложение4411,83497,25432,05454,0
Управляемое приложение4470,83584,85522,65541,0

В среднем платформа 8.2 на 25% быстрее, чем 8.3. Я немножко не ожидал такой разницы и решил провести тест на другой машине. Скажу только, что там 8.2 была быстрее процентов на 20.

Почему? Не знаю, дезасемблировать ядро в мои планы не входило, но в замер производительности я все же заглянул. Оказалось, что сами циклические операции в 8.3 проходят несколько быстрее, чем в 8.2. Но на строке

Код 1C v 8.х
 ПриемникТестовогоЗначения = ТестовыйМассив.Получить(н);   

то есть при считывании элемента коллекции в переменную происходит значительное снижение производительность.

Для себя я сделал несколько выводов:

1. Если есть возможность использовать специализированный цикл & «Для Каждого», то лучше использовать его. Кстати, сам по себе он отрабатывает дольше чем другие циклы, но скорость доступа к элементу коллекции у него на много выше.

2. Если заранее знаешь количество итераций & используй «Для По». «Пока» отработает медленнее.

3. Если использовать цикл «Если» & другие программисты тебя явно не поймут.


В статье использованы материалы с хабра и хелпме


Опубликовано на сайте: https://HelpF.pro
Прямая ссылка: https://HelpF.pro/faq8/view/1855.html