Параллельные вычисления для http-сервиса

19.11.20

База данных - HighLoad оптимизация

Процесс, поток, нить - в различных операционных системах и языках программирования это наименьшая единица программы. В платформе существует аналог: фоновое задание. Посмотрим, как его можно использовать для распараллеливания вычислений http-сервиса.

Скачать файлы

Наименование Файл Версия Размер
Параллельные вычисления для http-сервиса:
.cf 78,66Kb
5
.cf 1 78,66Kb 5 Скачать

Для начала на всякий случай вспомним, что регламентные и фоновые задания - это не совсем про одно и то же. Механизм регламентных заданий - это по сути планировщик, который будет запускать определенное фоновое задание с заданной периодичностью. Фоновое задание - это отдельный процесс (он же поток, он же нить, он же Жора, он же Гоша... не будем вдаваться в детали), исполняющий часть работы приложения. Фоновые задания мы можем создавать/запускать по мере необходимости, чем и воспользуемся для решения задачи ускорения вычислений.

В рассматриваемом примере имеем http-сервис, выполняющий вычисления, которые могут быть абсолютно любыми: от "2*2" до запросов к БД. Наш сервис будет вычислять квадрат для каждого из переданного в массиве числа. Если размерность массива превысит определенный порог, то будет применяться распараллеливание. В реальных задачах к этому лучше отнестись внимательно, т.к. из-за излишних накладных расходов на организацию параллелизма можно получить деградацию времени отклика сервиса. В качестве инструмента организации параллелизма будем использовать фоновые задания.

Зачастую ответом сервиса являются данные, отформатированные в определенной нотации. Никто не ограничивает нас в раздельном формировании частей общего ответа. Ведь мы можем сформировать несколько частей JSON, а затем объединить их в один большой результат? Конечно можем. Так и будем делать.

Ниже представлена основная логика с комментариями

//  
//  точка входа обработки тела запроса
//  тело запроса json-массив числовых значений [1, 2, 3, 4, 5, 6, 7]
//	результатом исполнения будет массив квадратов исходных чисел [1, 4, 9, 16, 25, 36, 49]
Функция Квадрат(ТелоЗапроса) Экспорт
    
    Результат = Новый Массив;    
    
    Попытка
        
        // получаем коллекцию из входных данных
        ВходящийМассив = ИзВеба(ТелоЗапроса);
        
        // служебная структура, которая содержит все необходимые для основного процесса вычислений параметры
        ПараметрыВычисления = СтруктураПараметровВычисления();
        ПараметрыВычисления.ВходящийМассив = ВходящийМассив;
        
        // определяем необходимость распараллеливания работы
        КоличествоНаПроцесс = 2;
        Если ВходящийМассив.Количество() > КоличествоНаПроцесс Тогда
            
            // реализуем параллельную обработку
            
            // разделяем данные на части
            МассивЧастей = РазделитьНаЧастиЗаданнойРазмерности(ВходящийМассив, КоличествоНаПроцесс);
            
            // формируем параметры исполнения для каждого процесса
            НомерПакета = 0;
            МассивПараметровФоновыхЗаданий = Новый Массив;
            Для Каждого Часть из МассивЧастей Цикл
                
                ТекущиеПараметрыВычисления = СтруктураПараметровВычисления();
                ТекущиеПараметрыВычисления.ВходящийМассив = Часть;
                ТекущиеПараметрыВычисления.НомерПакета = НомерПакета;
                
                ПараметрыФоновогоЗадания = Новый Массив;
                ПараметрыФоновогоЗадания.Добавить(ТекущиеПараметрыВычисления);
                МассивПараметровФоновыхЗаданий.Добавить(ПараметрыФоновогоЗадания);
                
                НомерПакета = НомерПакета + 1;
                
            КонецЦикла;
            
            // выполняем вычисления
            // для этого запускаем фоновые задания, ожидаем их завершения и собираем переданные сообщения
            // каждое сообщение - это кусочек вычисления, которые необходимо будет просто собрать вместе в один общий ответ
            МетодОбработки = "Вычислитель.ВычислитьКвадрат";
            СообщенияФоновыхПроцессов = ВыполнитьПроцессы(МетодОбработки, МассивПараметровФоновыхЗаданий);
            
            // получение и сортировка результата
            // сортировка может потребоваться, если результат сервиса должен быть собран в строго определенном порядке
            ТаблицаСообщений = Новый ТаблицаЗначений;
            ТаблицаСообщений.Колонки.Добавить("НомерПакета");
            ТаблицаСообщений.Колонки.Добавить("Результат");
            Для Каждого ТекущееСообщение Из СообщенияФоновыхПроцессов Цикл                
                ЗаполнитьЗначенияСвойств(ТаблицаСообщений.Добавить(), ИзВеба(ТекущееСообщение.Значение));
            КонецЦикла;
            ТаблицаСообщений.Сортировать("НомерПакета");
            
            // компоновка ответа сервиса
            Для Каждого ТекущееСообщение Из ТаблицаСообщений Цикл
                ТекущийРезультат = ТекущееСообщение.Результат;
                Для Каждого ТекущийЭлемент Из ТекущийРезультат Цикл
                    Результат.Добавить(ТекущийЭлемент);
                КонецЦикла;
            КонецЦикла;            
            
        Иначе
            
            // сюда попадаем, если не нужно распараллеливать            
            Результат = Вычислитель.ВычислитьКвадрат(ПараметрыВычисления);
            
        КонецЕсли;
                
    Исключение
        ОписаниеОшибки = ОписаниеОшибки();
        ЗаписьЖурналаРегистрации(ИмяСобытияЖурнала(), УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки);
    КонецПопытки;
    
    
    Возврат ВВеб(Результат);    
    
КонецФункции


А вот здесь можно увидеть, как фоновые задания передают результат исполнения

//
//
Функция ВычислитьКвадрат(ПараметрыВычисления) Экспорт
    
    Результат = Новый Массив;
    
    // выполняем вычисление квадрата для каждого числа
    Для Каждого Элемент Из ПараметрыВычисления.ВходящийМассив Цикл
        
        ТекущееЧисло = КакЧисло(Элемент);
        Результат.Добавить(ТекущееЧисло*ТекущееЧисло);
        
    КонецЦикла;
    
    // определяем необходимость передачи результата
    // если контекст текущего исполнения - это фоновое задание, то выполняется сообщение результата для родительского процесса
    Если ИсполнениеВФоновомПроцессе() Тогда
        
        Сообщение = Новый СообщениеПользователю;
        Сообщение.ИдентификаторНазначения = Новый УникальныйИдентификатор;
        Сообщение.Текст = ЗначениеВСтрокуВнутр(Новый ХранилищеЗначения(ВВеб(Новый Структура("НомерПакета, Результат", ПараметрыВычисления.НомерПакета, Результат)), Новый СжатиеДанных(9)));
        Сообщение.Сообщить();
        
    КонецЕсли;
    
    Возврат Результат;
    
КонецФункции

 

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

Чего мы можем добиться, используя описанный подход? В первую очередь быстродействие: параллельная обработка одного и того же набора данных может быть быстрее, чем последовательная. Во-вторых, надежность: уменьшение пакетов обработки может благотворно сказаться на показателях надежности. В-третьих, масштабируемость: можно распределять фоновые задания в кластере между нодами через ТНФ. 

Полная реализация описанного примера приведена во вложении к публикации. Конфигурация с примером (платформа 8.3.17).

http параллелизм фоновые задания параллельные вычисления

См. также

Оптимизация нагрузки на ЦП сервера СУБД используя типовые индексы

HighLoad оптимизация Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Анализ простого плана запроса. Оптимизация нагрузки на ЦП сервера СУБД используя типовые индексы.

13.03.2024    2963    spyke    26    

42

Быстродействие типовой 1С

HighLoad оптимизация Платформа 1С v8.3 Бесплатно (free)

Оказывается, в типовых конфигурациях 1С есть, что улучшить!

13.03.2024    5099    vasilev2015    19    

37

Анализируем SQL сервер глазами 1С-ника

HighLoad оптимизация Инструменты администратора БД Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Обработка для простого и удобного анализа настроек, нагрузки и проблем с SQL сервером с упором на использование оного для 1С. Анализ текущих зааросов на sql, ожиданий, конвертация запроса в 1с и рекомендации где может тормозить

1 стартмани

15.02.2024    7625    158    ZAOSTG    67    

96

Удаление строк из таблицы значений различными способами с замером производительности

HighLoad оптимизация Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Встал вопрос: как быстро удалить строки из ТЗ? Рассмотрел пять вариантов реализации этой задачи. Сравнил их друг с другом на разных объёмах данных с разным процентом удаляемых строк. Также сравнил с выгрузкой с отбором по структуре.

09.01.2024    5966    doom2good    48    

63

Опыт оптимизации 1С на PostgreSQL

HighLoad оптимизация Бесплатно (free)

При переводе типовой конфигурации 1C ERP/УТ/КА на PostgreSQL придется вложить ресурсы в доработку и оптимизацию запросов. Расскажем, на что обратить внимание при потерях производительности и какие инструменты/подходы помогут расследовать проблемы после перехода.

20.11.2023    8850    ivanov660    6    

76

ТОП проблем/задач у владельцев КОРП лицензий 1С на основе опыта РКЛ

HighLoad оптимизация Бесплатно (free)

Казалось бы, КОРП-системы должны быть устойчивы, быстры и надёжны. Но, работая в рамках РКЛ, мы видим немного другую картину. Об основных болевых точках КОРП-систем и подходах к их решению пойдет речь в статье.

15.11.2023    5097    a.doroshkevich    20    

72

Начните уже использовать хранилище запросов

HighLoad оптимизация Запросы

Очень немногие из тех, кто занимается поддержкой MS SQL, работают с хранилищем запросов. А ведь хранилище запросов – это очень удобный, мощный и, главное, бесплатный инструмент, позволяющий быстро найти и локализовать проблему производительности и потребления ресурсов запросами. В статье расскажем о том, как использовать хранилище запросов в MS SQL и какие плюсы и минусы у него есть.

11.10.2023    16166    skovpin_sa    14    

98
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. ltfriend 954 19.11.20 19:04 Сейчас в теме
Не стоит путать понятия "процесс" и "поток".
Процесс - экземпляр программы во время выполнения, независимый объект, которому выделены системные ресурсы (например, процессорное время и память).
Потоки (Thread) существуют внутри процесса. Есть основной поток, в котором выполняется основной код приложения, а также процесс может создавать дополнительные потоки, которые выполняются параллельно. Поток использует то же самое пространства стека, что и процесс.
А "нить" - просто прямой перевод слова Thread.
Другими словами, экземпляр запущенного сервера 1С - это процесс, фоновые задания, которые создаются и выполняются на этом сервере - потоки.
Shmell; VitaliyCeban; qwe322; van_za; +4 Ответить
2. NoRazum 29 03.12.20 11:40 Сейчас в теме
Увидел
"Попытка"
и большой кусок кода.
Дальше читать не стал.

Считаю эта плохим подходам.
(Хотя сама 1с очень любит так делать)
3. Lars Ulrich 614 03.12.20 15:23 Сейчас в теме
(2) Читать не призываю, но соглашусь с высказанной мыслью.
Здесь это сделано для того, чтобы не перегружать код проверкой входных данных. Тут ведь как: будешь смотреть на льва не увидишь золота, будешь смотреть на золото не увидишь льва.
4. NoRazum 29 03.12.20 17:40 Сейчас в теме
(3) Лев то хорош, Из-за этого поставил ПЛЮС.
Lars Ulrich; +1 Ответить
5. artbear 1448 03.12.20 19:02 Сейчас в теме
(0) Интересная публикация, спасибо!

одно но
Никогда так не пишите код обработки исключения
Исключение
        ОписаниеОшибки = ОписаниеОшибки();
        ЗаписьЖурналаРегистрации(ИмяСобытияЖурнала(), УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки);
КонецПопытки;

Так вы маскируете стек вызовов от администраторов, используйте подробное представление ошибки.
Стандарт 1С так и рекомендует.

подобная неточная обработка исключений - одна из самых частых ошибок\неточностей разработчиков.
очень часто наблюдаю такой код.
Lars Ulrich; +1 Ответить
9. efin 06.12.20 04:39 Сейчас в теме
(5) покажите, пожалуйста, пример кода. Потому что вот вроде слова знакомые, а общий смысл ускользает...
10. Lars Ulrich 614 08.12.20 10:51 Сейчас в теме
(9) имеется ввиду, что следует использовать подробное представление исключения.
Пример из синтаксис-помощника:
Попытка
    ...
Исключение
    Инфо = ИнформацияОбОшибке();
    Сообщить(НСтр("ru='Описание=';en='Description='") + Инфо.Описание + "'");
    Сообщить(НСтр("ru='ИмяМодуля=';en='ModuleName='") + Инфо.ИмяМодуля + "'");
    Сообщить(НСтр("ru='НомерСтроки=';en='LineNumber='") + Инфо.НомерСтроки + "'");
    Сообщить(НСтр("ru='ИсходнаяСтрока=';en='SourceLine='") + Инфо.ИсходнаяСтрока + "'");
КонецПопытки;
Показать


Источник для ознакомления: https://its.1c.ru/db/v8std/content/499/hdoc
ig77777; efin; +2 Ответить
11. artbear 1448 10.12.20 13:49 Сейчас в теме
(10) Да, спасибо за ссылку на стандарт.

Но пример кода из СП так себе (
вот как раз такое описание не нужно, а нужно как по ссылке на стандарт
6. artbear 1448 03.12.20 19:08 Сейчас в теме
(0) Ну и вообще при работе с фоновыми заданиями нужно помнить, что они могут быть прекращены в любой момент и могут не доработать до конца.
поэтому приходится использовать спец.обработку состояний фоновых заданий.

я для работы с фоновыми заданиями с гарантированным выполнением и гибкой настройкой много лет использую подсистему Менеджер Заданий моего товарища, Евгения Павлюка.
в ней многие проблемы работы с ФЗ решены.

Исходники есть на гитхабе, есть хорошая статья на habr-е
- https://github.com/wizi4d/TaskManagerFor1C
- https://habr.com/ru/post/255387/

Рекомендую.
Shmell; Lars Ulrich; +2 Ответить
7. artbear 1448 03.12.20 19:13 Сейчас в теме
(0) А почему юзаете возврат значений из ФЗ через СообщенияПользователю?
почему не штатный механизм 1С через временное хранилище, как написано в документации https://its.1c.ru/db/v8312doc#bookmark:dev:TI000000819

все-таки ненужная\излишня сериализация\десереализация
8. Lars Ulrich 614 04.12.20 12:06 Сейчас в теме
(7) можно и через механизм временного хранилища, будет побыстрее работать. соглашусь, что следовало бы в примере его использовать. Через СообщенияПользователю в текущей практике часто используется трансляция состояния исполнения фоновых заданий, как-то сходу его и задействовал.
user1503726; +1 Ответить
Оставьте свое сообщение