Какой цикл быстрее? Тестируем 1С

в 10:13, , рубрики: , высокая производительность, ненормальное программирование, Программирование, производительность, циклы

Занимаюсь программированием 1С уже несколько лет, и тут посетила мысль — «А не пройти ли какой-нибудь обучающий курс, вдруг в знаниях есть какие-то пробелы, о которых раньше даже и не подозревал»? Сказано-сделано. Сижу, слушаю курс, дохожу до циклических операторов и тут вторая мысль (да, не часто они у меня появляются)«А какой цикл быстрее»? Надо бы проверить.
Итак, я нашел пять способов, как можно организовать цикл средствами 1С.

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

Для н = 0 по КоличествоИтераций Цикл 
	КакиеТоДействия();
КонецЦикла;

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

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

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

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

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

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

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

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

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

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

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

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

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

ПриемникТестовогоЗначения = Элем;

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

Результаты для 8.3.5.1231
Интерфейс ДляПо ДляКаждого Пока Если
Обычное приложение 5734,2 4680,4 7235,4 7263,0
Управляемое приложение 5962,4 4882,6 7497,4 7553,6
Такси 5937,2 4854,6 7500,8 7513,0

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

Казалось бы и все, но — тестировать так тестировать!
Результат для платформы 8.2.19.106

Результаты для 8.2.19.106
Интерфейс ДляПо ДляКаждого Пока Если
Обычное приложение 4411,8 3497,2 5432,0 5454,0
Управляемое приложение 4470,8 3584,8 5522,6 5541,0

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

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

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

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

В итоге:

Процентное соотношение скорости циклов
ДляПо ДляКаждого Пока Если
78,97% 64,57% 99,57% 100,00%

К чему всё это? Для себя я сделал несколько выводов:

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

Автор: set2333

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js