Функции динамической группировки процессов составляют основу ключевых
подпрограмм PVM. Специализированная библиотека
libgpvm3 должна
компоноваться с пользовательскими программами, которые используют
любую групповую функцию; pvmd не реализует групповых функций.
Эта задача обрабатывается групповым сервером, который запускается
автоматически - при первом вызове групповой функции. Здесь приводятся
некоторые сведения о том, как могут обрабатываться группы в среде с
интерфейсом ``обмена сообщениями''. Выводы касаются эффективности
и надежности - в данном случае найден компромисс между статическими
и динамическими группами. Некоторые авторы приводят аргументы в пользу
того, что только задачи из группы могут пользоваться групповыми функциями.
Согласно философии PVM групповые функции разработаны как очень обобщенные и прозрачные для пользователя, что имеет определенную цену с точки зрения эффективности. Каждая задача PVM может присоединиться или покинуть любую группу в любое время - без необходимости информирования все другие задачи затрагиваемой группы. Задачи могут широковещательно передавать сообщения в группы, членами которых они не являются. В целом, любая задача PVM может вызвать любую из следующих функций во всякое время. Исключение составляют pvm_lvgroup(), pvm_barrier() и pvm_reduce(), природа которой требует членства вызывающей задачи в указанной группе.
int info = pvm_lvgroup( char *group)
call pvmfjoingroup( group, inum)
call pvmflvgroup( group, info)
Эти подпрограммы позволяют задаче присоединиться к именованной пользователем
группе и покинуть ее. Первый вызов
pvm_joingroup() создает
группу с именем group и включает в нее вызывающую задачу;
pvm_joingroup возвращает номер экземпляра процесса (inum)
в некоторой группе. Номера экземпляров находятся в диапазоне от нуля
до количества членов в группе - минус один. В PVM версии 3 одна задача
может состоять в нескольких группах.
Если процесс покинул группу и снова пытается присоединиться к ней, то он может получить другой номер экземпляра. Номера экземпляров перераспределяются, поэтому задача, состоящая в группе, будет получать наименьший из доступных номеров экземпляров. Однако если несколько задач состоят в группе, то не гарантируется, что задаче будет ``выдан'' ее предыдущий номер экземпляра.
Чтобы помочь пользователю в управлении последовательностью назначения номеров, независимо от того, происходит присоединение или отсоединение, функция pvm_lvgroup() не завершается до тех пор, пока задача не будет достоверно извещена об этом факте; pvm_joingroup(), вызываемая после этого, назначит вакантный номер экземпляра новой задаче. Ответственность за последовательность назначения номеров экземпляров накладывается на пользователя, если в алгоритме задачи возникает потребность в этом. Если несколько задач покинет группу и в ней не останется членов, то в номерах экземпляров возникнут ``пробелы''.
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)
call pvmfbarrier( group, count, info)
call pvmfbcast( group, msgtag, info)
int nitem, int datatype, int msgtag, char *group,
int root)
call pvmfreduce( func, data, count, datatype, msgtag,
group, root, info)
PvmMax, PvmMin, PvmSum, PvmProduct
Редуцирующая операция над входными данными выполняется поэлементно. Например, если массив данных состоит из двух чисел с плавающей запятой, а функция - это PvmMax, то результат будет включать два числа: глобальный максимум от всех членов группы - первое - и глобальный максимум ото всех - второе.
В дополнение, пользователи могут определять свои собственные функции для глобальных операций и указывать их в func. Пример дается в исходных текстах PVM. Обратитесь к PVM_ROOT/examples/ gexamples.
Функция pvm_reduce() не блокируется. Если задача вызывает pvm_reduce, а затем покидает группу до того, как результат pvm_reduce появится в root, то может произойти ошибка.