1с циклы. Перескакивание некоторых операций цикла

12 декабря 2014 в 13:13

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

  • Высокая производительность ,
  • Ненормальное программирование ,
  • Программирование

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

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

Для н = 0 по КоличествоИтераций Цикл КакиеТоДействия(); КонецЦикла;
Второй вид «ДляКаждого» :

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

Пока н <> КоличествоИтераций Цикл КакиеТоДействия(); н = н + 1; КонецЦикла;
Далее вспомнил ассемблерную молодость - цикл «Если» :

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

Процедура РекурсивныйЦикл(н, КоличествоИтераций) КакиеТоДействия(); Если н <> КоличествоИтераций Тогда РекурсивныйЦикл(н+1, КоличествоИтераций); КонецЕсли; КонецПроцедуры
Естественно, что относить рекурсию к циклам не совсем корректно, но тем ни менее с её помощью можно добиться похожих результатов. Сразу оговорюсь, что в дальнейшем тестировании рекурсия не участвовала. Во первых все тесты проводились при 1 000 000 итераций, а рекурсия выпадает уже при 2 000. Во вторых скорость рекурсии в десятки раз меньше, чем скорость остальных циклов.

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

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


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

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

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

Почему? Не знаю, дезасемблировать ядро в мои планы не входило, но в замер производительности я все же заглянул. Оказалось, что сами циклические операции в 8.3 проходят несколько быстрее, чем в 8.2. Но на строке
ПриемникТестовогоЗначения = ТестовыйМассив.Получить(н);
то есть при считывании элемента коллекции в переменную происходит значительное снижение производительность.

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

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

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

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

Для н = 0 по КоличествоИтераций Цикл КакиеТоДействия(); КонецЦикла;
Второй вид «ДляКаждого» :

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

Пока н <> КоличествоИтераций Цикл КакиеТоДействия(); н = н + 1; КонецЦикла;
Далее вспомнил ассемблерную молодость - цикл «Если» :

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

Процедура РекурсивныйЦикл(н, КоличествоИтераций) КакиеТоДействия(); Если н <> КоличествоИтераций Тогда РекурсивныйЦикл(н+1, КоличествоИтераций); КонецЕсли; КонецПроцедуры
Естественно, что относить рекурсию к циклам не совсем корректно, но тем ни менее с её помощью можно добиться похожих результатов. Сразу оговорюсь, что в дальнейшем тестировании рекурсия не участвовала. Во первых все тесты проводились при 1 000 000 итераций, а рекурсия выпадает уже при 2 000. Во вторых скорость рекурсии в десятки раз меньше, чем скорость остальных циклов.

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

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


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

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

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

Почему? Не знаю, дезасемблировать ядро в мои планы не входило, но в замер производительности я все же заглянул. Оказалось, что сами циклические операции в 8.3 проходят несколько быстрее, чем в 8.2. Но на строке
ПриемникТестовогоЗначения = ТестовыйМассив.Получить(н);
то есть при считывании элемента коллекции в переменную происходит значительное снижение производительность.

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

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

Рейтинг: / 0

Встречаем новую трудовую неделю очередным выпуском обучающего урока. Сегодня мы рассмотрим, что собой представляют циклы в «1С:Підприємство».

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

Циклы бывают нескольких видов. Первый вид «Пока»:

Пока <Какое-то условие> Цикл

//Ваш исполняемый код

КонецЦикла;

Это самая простая конструкция. Условием может быть либо равенство, либо значение булево и т.д.

Например:

Запишем переменную перед началом цикла «Перем1» - она у нас будет выступать в роли ограничителя цикла.

Затем укажем в цикле условие «Перем1 <> 5», т.е. цикл будет выполняться пока наша переменная не примет значение число пять.

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

Листинг 1. Простой цикл Пока:

У циклов есть несколько основных операторов.

Прервать – прерывает полностью цикл. В указанной строке.

Листинг 2. Простой цикл Пока Прервать:

Продолжить – после попадания на эту строчку, платформа вернется в начало цикла. А все что после этой строки не будет выполнено.

Листинг 3. Простой цикл Пока Продолжить:

Следующий тип цикла «Для каждого из»

Для каждого <имя переменной> ИЗ <Имя объекта для перебора> Цикл

//Ваш исполняемый код

КонецЦикла;

Такой цикл хорошо подходит для перебора строк таблицы значений и прочих универсальных коллекций, таких как массив или список значений. Давайте сделаем пример. Создадим таблицу значений, заполним ее несколькими строками и колонками и сообщим результат.

Листинг 4. Простой цикл Для каждого:


При таком цикле, переменная «Стр» записала в себя значение «СтрокаТаблицыЗначений». В этой переменной, через точку, можно обратиться к колонкам и их значениям.

Следующий тип цикла «Для По» похож на предыдущий цикл. Имеет такую конструкцию:

Для <имя переменной> = <Выражение 1> ПО <Выражение 2> Цикл

//Ваш исполняемый код

КонецЦикла;

Такой цикл менее распространён и служит для выполнения цикла по определенному количеству подходов, указанных в<Выражение 2>. Сделаем пример.

21
//Функция формирует удобное для чтения представление значений. // Примеры форматирования чисел ЗначФормат = Формат(123456.789, " ЧЦ=10; ЧДЦ=2"); // ЗначФормат = " 123 456,79" ЗначФормат = Формат(123456.789, " ЧГ=0; ЧДЦ=2"); // Знач 16
Полнотекстовый поиск - позволит найти текстовую информацию, размещенную практически в любом месте используемой конфигурации. При этом искать нужные данные можно либо по всей конфигурации в целом, либо сузив... 8
" Момент времени" - виртуальное поле, не хранится в базе данных. Содержит объект МоментВремени (который включает в себя дату и ССЫЛКУ НА ДОКУМЕНТ) В 7.7 было понятие ПозицияДокумента, а в 8.x Момент времени Для получения... 6
Для 8.х НайтиПоСсылкам (FindDataByRef) Синтаксис: НайтиПоСсылкам (Список ссылок) Параметры: Список ссылок Обязательный Тип: Массив. Массив со списком ссылок на объекты, ссылки на которые нужно найти. ...

Описание:

Оператор цикла Для предназначен для циклического повторения операторов, находящихся внутри конструкции Цикл – КонецЦикла .

Перед началом выполнения цикла значение Выражение 1 присваивается переменной Имя_переменной . Значение Имя_переменной автоматически увеличивается при каждом проходе цикла. Величина приращения счетчика при каждом выполнении цикла равна 1.

Цикл выполняется, пока значение переменной Имя_переменной меньше или равно значению Выражение 2 . Условие выполнения цикла всегда проверяется в начале, перед выполнением цикла.

Синтаксис:

Параметры:

Имя_переменной Идентификатор переменной (счетчика цикла), значение которой автоматически увеличивается на 1 при каждом повторении цикла. Так называемый счетчик цикла. Выражение 1 Числовое выражение, которое задает начальное значение, присваиваемое счетчику цикла при первом проходе цикла. По Синтаксическая связка для параметра Выражение 2 . Выражение 2 Максимальное значение счетчика цикла. Когда переменная Имя_переменной становится больше чем Выражение 2, выполнение оператора цикла Для прекращается. Цикл Операторы, следующие за ключевым словом Цикл выполняются, пока значение переменной Имя_переменной меньше или равно значения Выражение 2 .

Цикл Для Каждого

Описание:

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

Синтаксис:

Параметры:

Имя_переменной_1 Переменная, которой при каждом повторении цикла присваивается значение очередного элемента коллекции. Из Синтаксическая связка для параметра Имя_переменной_2 . Имя_переменной_2 Переменная или выражение, предоставляющее коллекцию. Элементы этой коллекции будут присваиваться параметру Имя_переменной_1 . Цикл Операторы, следующие за ключевым словом Цикл выполняются для каждого элемента коллекции. // Операторы Исполняемый оператор или последовательность таких операторов. Прервать Позволяет прервать выполнение цикла в любой точке. После выполнение этого оператора управление передается оператору, следующему за ключевым словом КонецЦикла . Продолжить Немедленно передает управление в начало цикла, где производится вычисление и проверка условий выполнения цикла. Операторы, следующие в теле цикла за ним, на данной итерации обхода не выполняются. КонецЦикла Ключевое слово, которое завершает структуру оператора цикла.

Цикл Пока

Описание:

Оператор цикла Пока предназначен для циклического повторения операторов, находящиеся внутри конструкции Цикл – КонецЦикла . Цикл выполняется, пока логическое выражение равно Истина . Условие выполнения цикла всегда проверяется вначале, перед выполнением цикла.

Синтаксис:

Параметры:

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