Функции динамической группировки процессов составляют основу ключевых
подпрограмм ПВМ. Специализированная библиотека libgpvm3 должна
прикомпановываться к пользовательским программам, которые используют
любую из групповых функций. pvmd не реализует групповых функций.
Эта задача обрабатывается групповым сервером, который запускается
автоматически - при первом вызове групповой функции. Здесь приводятся
некоторые дебаты о том, как могут обрабатываться группы в среде с
интерфейсом "обмена сообщениями". Выводы касаются эффективности
и надежности - в данном случае найден компромисс между статическими
и динамическими группами. Некоторые авторы приводят аргументы в пользу
того, что только задачи из группы могут пользоваться групповыми функциями.
Придерживаясь философии ПВМ, групповые функции разработаны как очень
обобщенные и прозрачные для пользователя, что имеет определенную цену
с точки зрения эффективности. Каждая задача ПВМ может присоединиться
или покинуть любую группу в любое время - без необходимости информирования
всех других задач затрагиваемой группы. Задачи могут широковещательно
передавать сообщения в группы, членами которых они не являются. В
целом, любая задача ПВМ может вызвать любую из следующих функций в
любое время. Исключение составляют pvm_lvgroup(), pvm_barrier()
и pvm_reduce(), чья природа требует членства вызывающей
задачи в указанной группе.
int inum = pvm_joingroup(char *group)
int info = pvm_lvgroup( char *group)
call pvmfjoingroup( group, inum)
call pvmflvgroup( group, info)
Эти подпрограммы позволяют задаче присоединиться к именованной пользователем
группе и покинуть ее. Первый вызов pvm_joingroup() создает
группу с именем group и включает вызывающую задачу в нее.
pvm_joingroup возвращает номер экземпляра процесса (inum)
в некоторой группе. Номера экземпляров могут быть в диапазоне от нуля
до количества членов в группе минус один. В ПВМ версии 3, одна задача
может состоять в нескольких группах.
Если процесс покинул группу и снова пытается присоединиться к ней,
то он может получить другой номер экземпляра. Номера экземпляров перераспределяются,
поэтому задача, состоящая в группе, будет получать наименьший из доступных
номеров экземпляров. Но если несколько задач состоят в группе, то
не гарантируется, что задаче будет "выдан" ее предыдущий номер
экземпляра.
Чтобы помочь пользователю в управлении последовательностью назначения
номеров, не зависимо от того, происходит присоединение или отсоединение,
функция pvm_lvgroup() не завершается до тех пор, пока задача
не будет достоверно извещена об этом факте. pvm_joingroup(),
вызываемая после этого, назначит вакантный номер экземпляра новой
задаче. Ответственность за последовательность назначения номеров экземпляров
накладывается на пользователя, если возникает потребность в алгоритме.
Если несколько задач покинет группу и в ней не останется членов, то
в номерах экземпляров возникнут "пробелы".
int tid = pvm_gettid( char *group, int inum)
int inum = pvm_getinst( char *group, int tid)
int size = pvm_gsize( char *group)
call pvmfgettid( group, inum, tid)
call pvmfgetinst( group, tid, inum)
call pvmfgsize( group, size)
Подпрограмма pvm_gettid() возвращает TID процесса
с данными именем группы и номером экземпляра. pvm_gettid()
позволяет двум не знающим друг друга задачам получить TID
друг друга посредством присоединения к общей группе. Подпрограмма
pvm_getinst возвращает номер задачи с TID из указанной
группы. Подпрограмма pvm_gsize возвращает количество членов
в указанной группе.
int info = pvm_barrier( char *group, int count)
call pvmfbarrier( group, count, info)
Вызовом pvm_barrier() процесс блокируется до тех пор, пока
count членов группы не вызовут pvm_barrier. В общем,
count должен совпадать с количеством членов в группе. Счетчик
требуется потому, что при динамической группировке процессов, ПВМ
не может знать, сколько процессов имеется в группе в данный момент
времени. Ошибочным действием для процесса будет вызов pvm_barrier
с указанием группы, членом которой он не является. Также ошибочными,
при данной барьерной синхронизации, будут вызовы с несовпадающими
аргументами count. Например, ошибка возникает, если один
член группы вызывает pvm_barrier() со счетчиком, равным
4, а другой член вызывает pvm_barrier со счетчиком, равным
5.
int info = pvm_bcast( char *group, int msgtag)
call pvmfbcast( group, msgtag, info)
pvm_bcast() метит сообщение целочисленным идентификатором
msgtag и широковещательно передает его всем задачам в указанной
группе, за исключением "собственной" задачи (если она является
членом группы). Для pvm_bcast() термин "все задачи"
определяется как: те задачи, которые сервер группы считает находящимися
в группе во время вызова подпрограммы. Если задача присоединяется
к группе при широковещательном вызове, то сообщение она может не получить
вообще. Если задача покидает группу при широковещательном вызове,
то копия сообщения по-прежнему будет ей передаваться.
int info = pvm_reduce( void (*func()), void *data, int nitem,
pvm_reduce() выполняет глобальную арифметическую операцию
в группе, например нахождение глобальной суммы или глобального максимума.
Результат редуцирующей операции помещается в root. ПВМ поддерживает
четыре предопределенные функции, которые пользователь может передать
через func. Это следующие.
PvmMax
PvmMin
PvmSum
PvmProduct
Редуцирующая операция над входными данными выполняется поэлементно.
Например, если массив данных состоит из двух чисел с плавающей запятой,
а функция - это PvmMax, то результат будет включать два числа:
глобальный максимум от всех членов группы - первое - и глобальный
максимум ото всех - второе.
В дополнение, пользователи могут определять свои собственные функции
для глобальных операций и указывать их в func. Пример дается
в исходных текстах ПВМ. Обратитесь к PVM_ROOT/examples/gexamples.
Примечание. pvm_reduce() не блокируется. Если задача вызывает
pvm_reduce, а затем покидает группу до того, как результат
pvm_reduce появится в root, то может произойти
ошибка.
Знаете ли Вы, что апостериорное решение в стохастических двухэтапных моделях - это вектор оптимальных значений переменных, характеризующих плановые задания, выполняемые после поступления информации о наступлении определённого случайного события, влияющего на хозяйственные результаты. Для каждого варианта (исхода) случайных событий предусматривается отдельное апостериорное решение.