|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Здравствуйте. Я знаком с Cache не так давно и профессионально ей не занимался. Однако для попадания в одну компанию мне нужно было написать на ней некоторые классы, моделирующие сущности интернет-магазина. При знакомстве с Cache я был несколько удивлен её синтаксисом, однако в последствии я к нему привык. В ту компанию я так и не попал, однако старший товарищ, некоторое время работавший там, предложил мне сделать небольшую работу на Cache. Был сервер, на котором крутилось какое-то приложение для формирования отчетов, написанное на Cache. Время формирования отчета из большого объема данных составлял минуты, то есть довольно длительное время. Приложение запускалось на определенное время(если не ошибаюсь, ночью), и так как отчетов обычно приходилось формировать много, иногда оно не успевало завершить их все. Остальное время сервер был занят более важными делами. Как все делалось – просто jobался процесс, который выгребал из БД все запросы на отчеты, и начинал их по очереди обрабатывать, основываясь на данных, хранящихся так же в БД. Это кажется довольно нерациональным. Мне было предложено попробовать написать решение, которое бы позволило распараллелить обработку данных и формирование на их основании отчетов, ну или в принципе любой другой похожей задачи. Сразу появилась мысль написать что то типа ThreadPool-а на Cache. Однако, как оказалось, в Cache создавать потоки нельзя, и команда job создает вполне себе полноценный процесс. Погуглив, я нашел на sql.ru данную тему: http://www.sql.ru/forum/656456/pul-potokov. Там говорится в том числе, что использование такого подхода нерационально для Cache, так как единственным возможным способом синхронизации процессов является использование блокируемой глобальной очереди задач. А блокировка глобала – одна из самых ресурсоемких задач. И это правда, этот подход не стоит использовать, если выполняемая операция длится малое количество времени. Однако в данном случае, когда время выполнения операции может составлять минуты, этот подход более чем оправдан. Так же длительное время обработки диктует необходимость выполнения операций асинхронно. Но, конечно же, необходимо принять во внимание все возможные подходы к распараллеливанию обработки. В написанном мною решении первый подход – использование одного процесса для обработки, как в той компании. Второй – запуск для каждой задачи отдельного обрабатывающего процесса из главного процесса. Третий – как раз подобие пула потоков, с фиксированным количеством процессов и глобальной блокируемой очередью. И четвертый – так же подобие пула процессов, однако уже с динамическим созданием и уничтожением рабочих процессов, что может оказаться полезным для экономии ресурсов сервера или когда на сервере запущено не только это приложение. Алгоритм и данные для формирования отчета мне конечно никто не дал. Немного подумав, решил просто считать факториал большого числа, которое можно помещать в запрос для обработки. Поддержки длинной арифметики в Cache не нашел, поэтому написал сам. Что представляет из себя решение. Оно содержит веб сервис, который принимает запросы(в моем случае числа, факториал которых надо найти) и сохраняет их БД. Класс веб сервиса называется ThreadPoolService. Так же в решении присутствует хранимый класс Request, содержащий поля для самого запроса(числа), статуса запроса, времени добавления и завершения выполнения и результата выполнения. Класс ThreadPool содержит в себе методы класса для создания и управления рабочими процессами, а так же методы класса для настройки решения(метод обработки, инициализация служебных данных и т.д) и запуска и остановки обработки. Класс Handler содержит логику работы с запросами и их обработку. Обрабатывающее приложение, собственно, состоит из классов ThreadPool и Handler и написано в процедурном стиле(на методах класса) и помещено в классы только для логического разделения. Как показало небольшое тестирование, использование восьми рабочих процессов на моем отнюдь не топовом двухъядерном ноутбуке дало сокращение общего времени обработки фиксированного набора запросов в 2 раза. Что, на мой взгляд, является неплохим показателем. Теперь немного про динамическое создание и уничтожение процессов. Я решил, что критерием для запуска нового рабочего процесса является увеличение количества запросов в глобальной очереди. Я не стал писать особо сложной логики для проверки динамики роста очереди. Управляющий созданием рабочих процессов процесс через определенный интервал времени смотрит на размер очереди, запоминает его и сравнивает со среднеарифметическим двух прошлых замеров. Если текущий замер больше среднего значения, запускается новый процесс. Это примитивная логика и никто не помешает вам переписать её под себя. Уничтожением рабочих процессов занимаются сами рабочие процессы. Если процесс, обращаясь к глобальной очереди, в течение определенного(настраиваемого) промежутка времени систематически не получает оттуда идентификатора запроса, он завершается. Вот, собственно и все, что хотелось бы рассказать о решении. Оно находится тут: https://github.com/OVME/cacheAsyncService, экспортированное в xml и так же в файлах .txt, если кто-то захочет просто посмотреть, не импортируя в студию. Так же к решению приложен файл с инструкцией по использованию решения. Если вам не захочется писать клиентский код для веб-сервиса, то тут - https://github.com/OVME/Testclient-1.0 есть тестовый клиент на .NET. Надеюсь, кому-нибудь пригодится, так как с Cache непосредственно на работе я дела не имею, а технология мне понравилась и хотелось бы сделать что-нибудь полезное для её пользователей. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 10:23 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Иван Салов , ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 10:38 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
servit, Про синхронизацию процессов я читал, и в примере, приводимом там, происходит синхронизация с главным процессом. В моей задаче такой синхронизации не требуется, относительно главного процесса обработка происходит как раз таки асинхронно, а рабочие процессы координируются между собой для получения задачи из очереди, и использование Cache Event Api для этого кажется мне не лучшим решением. Что касается %SYSTEM.WorkMgr - это более универсальное решение, удобное для запуска нескольких типов задач в рамках одной программы. В то время как мое решение более заточено именно на обработку данных из БД и возможность более расширено управлять настройками типа количества процессов, динамического изменения их количества, времени бездействия и т.п. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 11:32 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Т.е. вы сделали ensemble ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 12:02 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
DAiMor, конечно же нет. Такое решение нужно для тех задач, тип которых я уже описал. Зачем использовать Ensemble, если вам достаточно одной Cache и подобного решения для их выполнения. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 12:21 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Иван Салов , Скачал, импортировал и скомпилировал серверную часть. Что нужно теперь вызвать в терминале, чтобы увидеть результат вычисления в разных режимах работы факториала? ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 13:29 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Есть все же впечатление, что Вы сделали именно замену %SYSTEM.WorkMgr, который и есть менеджер для управление количеством фоновых обработчиков (в зависимости от присутствующего количества процессоров или некоторого форсированного в данном процессе значения ^||%ISC.WorkQueueMgr("ForceJobs")) И есть впечатление что передача управляющих событий через $system.Event.* - немного более эффективна, чем через SOAP/REST. И методическое замечание - при динамическом увеличении очереди на исполнение не стоит увеличивать количество параллельных обработчиков больше количества ядер (скажем умноженного на фактор меньше 2), от этого общее время исполнения не улучшится, а скорее ухудшится, т.к. будет CPU overcommit. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 13:39 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Timur Safin, SOAP тут не при чем, это лишь средство для добавления запросов в базу(так было удобнее тестировать) и возвращения результатов и к управляющим сообщениям отношения вовсе не имеет. И я согласился бы по поводу количества процессов и ядер, если бы тесты не показали несколько иное. Как не странно, максимальное снижение среднего времени обработки было показано при большем количестве процессов, нежели количество ядер. В идеале, конечно, вы правы и производительность будет расти только при количестве процессов<количества ядер. Но на деле, по всей видимости, на это повлияли и блокировки глобальной очереди и другие запущенные на компьютере приложения(увы, это мой домашний ноутбук). По факту на двух ядрах при использовании двух процессов уменьшения времени обработки по сравнению с одним почти не наблюдалось. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 14:06 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
servit, вам нужно сохранить запрос(Request) в БД, лучше это делать через веб-сервис. затем вызываете метод класса Initialize класса ThreadPool, выполняете необходимые настройки как написано в ReadMe и вызываете метод класса Start. Что бы получить результат обработки, вызываете метод Result с идентификатором, который вам вернул сервис при добавлении запроса. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 14:10 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Иван Салов вам нужно сохранить запрос(Request) в БД, лучше это делать через веб-сервис. затем вызываете метод класса Initialize класса ThreadPool, выполняете необходимые настройки как написано в ReadMe и вызываете метод класса Start. Что бы получить результат обработки, вызываете метод Result с идентификатором, который вам вернул сервис при добавлении запроса.Спасибо, вопрос снят. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 14:17 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Иван СаловПоддержки длинной арифметики в Cache не нашел, поэтому написал сам.Зачем она в СУБД, учитывая невысокую скорость p-code в арифметических операциях по сравнению с native-code? Для этих целей из Caché лучше вызывать готовый код Java/.NET через соответствующие Шлюзы или сразу на С/С++. Вряд ли Вы добъётесь на чистом COS со всеми ухищрениями и распараллеливанием скорости вычисления 50000! за 0.9сек и менее . ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 16:23 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
servit, кажется вы не поняли того, что я хотел донести. я не распараллеливаю вычисление факториала, это было бы глупо делать в рамках Cache. Вычисление факториала является лишь длительной вычислительной задачей. Распараллеливание же идет в программе для обработки запросов из базы, то есть появляется возможность обрабатывать(вычислять факториал в моем случае) сразу нескольких запросов, используя для этого определенное количество процессов. Несколько процессов не считают факториал одного числа. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 16:59 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Иван Саловкажется вы не поняли того, что я хотел донести.Надеюсь, что только я один. ... |
|||
:
Нравится:
Не нравится:
|
|||
11.05.2015, 17:08 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Timur SafinИ методическое замечание - при динамическом увеличении очереди на исполнение не стоит увеличивать количество параллельных обработчиков больше количества ядер (скажем умноженного на фактор меньше 2), от этого общее время исполнения не улучшится, а скорее ухудшится, т.к. будет CPU overcommit. Это не совсем так. Запросы обычно жрут не процессорное время, а работу с диском. Несколько запущенных процессов даже на одном ядре могут ускорить общее выполнение за счет обращение к кэшу. Весь вопрос в том насколько одинаковые или разные данные нужны процессам. ... |
|||
:
Нравится:
Не нравится:
|
|||
12.05.2015, 01:18 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
velmarЭто не совсем так. Запросы обычно жрут не процессорное время, а работу с диском.С каких пор обращение к диску теперь стало в обход работы процессора. Любая работа происходит с ним, дело только в том как часто он переключается между процессами, и сколько он может отдать процессорного времени каждому процессу. И по поводу обычно работа с диском, это опять же зависит от задач. у нашем проекте например много битовых индексов, и соответственно работы для процессора предостаточно, чтобы выполнять все необходимые логические условия. ... |
|||
:
Нравится:
Не нравится:
|
|||
12.05.2015, 08:13 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Как уже сказал это выше Дима, отношение "время-процессора"/"время в syscall на работу с диском" сильно зависит от используемой структуры данных и алгоритмов. Ваша задача, очевидно много времени проводит в syscall, посему квант времени на процессор текущей задачи уменьшается. Вполне допускаю, что множитель в вашем случае значительно больше 2. Но он все равно конечен (скажем после 4-8 обработчиков на одно ядро) у вас накладные расходы на поддержание многозадачности и переключение контекста станут вытеснять полезное процессорное время. Я предлагаю по примеру Bench^%SYS.WorkQueueMgr(start,end) сделать вам простой тестик, который бы оценивал как быстро данный компьютер может запустить от start до end задачек в параллель на вашей инфраструктуре. При идеальной "работе", когда мы мерим только накладные расходы на запуск и сбор результатов. Ну и сравнить полученные цифры с %SYSTEM.WorkMgr. Будет интересно ... |
|||
:
Нравится:
Не нравится:
|
|||
12.05.2015, 12:37 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Timur SafinВаша задача, очевидно много времени проводит в syscallЧто, вполне возможно, связано с тем, что слишком мал кэш данных. Типичный случай, когда используется автоматическая конфигурация памяти в Cache. ... |
|||
:
Нравится:
Не нравится:
|
|||
12.05.2015, 14:04 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
velmarВесь вопрос в том насколько одинаковые или разные данные нужны процессам. На самом деле, каждый процесс, обращаясь к глобальной очереди, тянет от туда идентификатор запроса. При этом обычно они(идентификаторы) идут по порядку. Т.е. обращаясь к таблице с запросами, каждый следующий процесс с наибольшей вероятностью возьмет себе следующий по номеру запрос. Поэтому да, это наверняка немало зависит от кэша. ... |
|||
:
Нравится:
Не нравится:
|
|||
12.05.2015, 15:26 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Timur SafinЯ предлагаю по примеру Bench^%SYS.WorkQueueMgr(start,end) Не забывайте, что исходники от нас скрыты, поэтому без них этот пример мало что говорит, разве что, оказывается, есть такая программка. ... |
|||
:
Нравится:
Не нравится:
|
|||
12.05.2015, 15:27 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Timur SafinЯ предлагаю по примеру Bench^%SYS.WorkQueueMgr(start,end) сделать вам простой тестик, который бы оценивал как быстро данный компьютер может запустить от start до end задачек в параллель на вашей инфраструктуре. Да, я попробую сделать это и выложу результаты. ... |
|||
:
Нравится:
Не нравится:
|
|||
12.05.2015, 15:30 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Timur Safin, только вот извините за недалекость, что это за пример? ... |
|||
:
Нравится:
Не нравится:
|
|||
12.05.2015, 15:33 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Timur Safin Ваша задача, очевидно много времени проводит в syscall, посему квант времени на процессор текущей задачи уменьшается. Вполне допускаю, что множитель в вашем случае значительно больше 2. Но он все равно конечен (скажем после 4-8 обработчиков на одно ядро) у вас накладные расходы на поддержание многозадачности и переключение контекста станут вытеснять полезное процессорное время. Тимур, я ничего не говорил о своей задаче :) А вот по поводу накладных расходов на поддержанию многозадачности хотелось бы узнать подробности. В 1997 году, когда я познакомился с Cache' (тогда еще OpenM), она прекрасно справлялась с более чем 50-ю пользователями на одноядерном процессоре и накладные расходы явно не превышали полезную нагрузку (каждого пользователя обслуживал отдельный процесс cache'). Неужели с тех пор все стало гораздо хуже? ... |
|||
:
Нравится:
Не нравится:
|
|||
13.05.2015, 02:14 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
50 пользователей не означает 50 процессов, загружающих процессор. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.05.2015, 05:30 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Это простой пример, который замеряет скорость запуска воркер-ов через $system.WorkMgr.Queue c ожиданием в queue.WaitForComplete. Запускаемая задача очень простая - `For i=1:1:10000 Set a=i` Могу прислать исходник вовлеченных в дело 2х подпрограмм. Если очень хотите посмотреть на эти 10 строчек кода (ок, 14 строчек) то пришлите письмо, или обратитесь в WRC. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.05.2015, 13:56 |
|
Параллельная обработка данных в нескольких процессах
|
|||
---|---|---|---|
#18+
Блок А.Н.50 пользователей не означает 50 процессов, загружающих процессор. Я же написал - под каждого пользователя создавался свой процесс. Так что в этом конкретном случае - означает. ... |
|||
:
Нравится:
Не нравится:
|
|||
13.05.2015, 23:17 |
|
|
start [/forum/topic.php?fid=39&msg=38955696&tid=1556669]: |
0ms |
get settings: |
7ms |
get forum list: |
12ms |
check forum access: |
2ms |
check topic access: |
2ms |
track hit: |
35ms |
get topic data: |
11ms |
get forum data: |
3ms |
get page messages: |
52ms |
get tp. blocked users: |
1ms |
others: | 290ms |
total: | 415ms |
0 / 0 |