next up previous contents
Next: Функция All-Reduce Up: Глобальные операции редукции Previous: MINLOС и MAXLOС   Contents

Операции, определяемые пользователем

Синтаксис функции MPI_OP_CREATE представлен ниже.

MPI_OP_CREATE(function, commute, op)

IN function определяемая пользователем операция (функция)
IN commute true, если операция коммутативна; иначе false.
OUT op операция (дескриптор)

int MPI_Op_create(MPI_User_function *function, int commute, MPI_Op *op)

MPI_OP_CREATE(FUNCTION, COMMUTE, OP, IERROR)
EXTERNAL FUNCTION
LOGICAL COMMUTE
INTEGER OP, IERROR

void MPI::Op::Init(User_function* function, bool commute)

Функция MPI_OP_CREATE связывает определенную пользователем глобальную операцию с указателем op, который впоследствии может быть использован в MPI_REDUCE, MPI_ALLREDUCE,
MPI_SCATTER
и MPI_SCAN. Определенная пользователем операция предполагается ассоциативной. Если commute = true, то операция должна быть как коммутативной, так и ассоциативной. Если commute = false, то порядок операндов фиксирован, операнды располагаются по возрастанию номеров процессов, начиная с нулевого. Порядок оценки может быть изменен, чтобы использовать преимущество ассоциативности операции. Если commute = true, то порядок оценки может быть изменен, чтобы использовать достоинства коммутативности и ассоциативности.

function - определенная пользователем функция, которая должна иметь следующие аргументы: invec, inoutvec, len и datatype.

В ANSI Си прототип для этой функции следующий:

typedef void MPI_User_function(void *invec,
void *inoutvec, int *len, MPI_Datatype *datatype);

Декларация определяемой пользователем функции для языка ФОРТРАН дана ниже:

SUBROUTINE USER_FUNCTION(INVEC, INOUTVEC, LEN, TYPE) <type> INVEC(LEN), INOUTVEC(LEN) INTEGER LEN, TYPE

Аргумент datatype - это дескриптор типа данных, которые были посланы в вызове MPI_REDUCE. Пользовательская функция редукции должна быть написана так, чтобы удовлетворять следующему: пусть u[0], ..., u[len-1] - len элементов в буфере обмена, описанных параметрами invec, len и datatype при вызове функции; пусть v[0], ..., v[len-1] - len элементов в буфере обмена, описанных аргументами inoutvec, len и datatype при вызове функции; пусть w[0], ..., w[len-1] - len элементов в буфере обмена, описанных параметрами inoutvec, len и datatype, когда функция возвращает управление; тогда w[i] = u[i] $\bigcirc $ v[i], для i=0, ... . len-1, где $\bigcirc $ -операция редукции, которую вычисляет функция.

Неформально, можно считать inoutvec и invec массивами из len элементов, которые комбинирует function. Результат операции редукции перезаписывает величины в inoutvec, что и определяет имя. Результатом каждого вызова функции является поточечная оценка оператором редукции len элементов: т.е. функция возвращает в inoutvec[i] значение invec[i] $\bigcirc $ inoutvec[i] для i = 0, ..., count-1, где $\bigcirc $ - операция объединения, считающаяся функцией.

Объяснение: Аргумент len позволяет MPI_REDUCE избежать вызова функции для каждого элемента во входном буфуре. Предпочтительнее, чтобы система могла применять функцию к порциям входа. На языке Си это передается по ссылке для совместимости с языком ФОРТРАН.

Сравнение значения параметра datatype с известными показывает, что возможно использование отдельных определенных пользователем функций для нескольких отличающихся данных.[]

В функции пользователя можно использовать общие типы данных, однако, если они не непрерывны, то скорее всего это приведет к неэффективности.

Никакая функции обмена MPI не может вызываться из пользовательской функции. Из пользовательской функции в случае ошибки может быть вызвана функция MPI_ABORT.

Совет пользователям: Предположим, что создается библиотека определяемых пользователем функций редукции, которые являются перегруженными: параметр datatype используется, чтобы выбрать правильный путь выполнения при каждом вызове, согласно типам операндов. Определяемая пользователем функция не может ``декодировать'' параметр datatype, который ей передан, и не может определить самостоятельно соответствие между дескрипторами типа данных и типом данных, который они представляют. Это соответствие было установлено, когда типы данных создавались. Прежде чем использовать библиотеку, должна быть выполнена преамбула библиотечной инициализации. Код преамбулы определит типы данных, которые используются библиотекой, и сохранит дескрипторы этих типов данных в глобальных статических переменных, которые разделяются кодом пользователя и библиотечным кодом.

Версия MPI_REDUCE для языка ФОРТРАН вызовет определяемую пользователем функцию, используя соглашение о вызовах языка ФОРТРАН и передаст аргумент типа данных ФОРТРАН-типа; версия языка Си использует соглашение о вызовах Си и Си-представление дескриптора типа данных. Пользователи, которым придется использовать смешение языков, должны определять свои функции соответственно.[]

Совет разработчикам: Приведем пример наивной и неэффективной реализации MPI_REDUCE.

if (rank > 0) { RECV(tempbuf, count, datatype, rank-1,...) User_reduce(tempbuf, sendbuf, count, datatype) } if (rank < groupsize-1) { SEND(sendbuf, count, datatype, rank+1, ...) } /* результат, который размещается в процессе groupsize-1 ... теперь посылается в корневой процесс */ if (rank == groupsize-1) { SEND(sendbuf, count, datatype, root, ...) } if (rank == root) { RECV(recvbuf, count, datatype, groupsize-1,...) }

Выполнение операции редукции происходит последовательно, от процесса 0 до процесса
group-size-1. Этот порядок выбран, чтобы учесь возможную некоммутативность оператора, определенного функцией User_reduce(). Более эффективную реализацию можно получить, если использовать преимущество ассоциативности и логарифмическую древовидную редукцию. Коммутативность может быть использована как преимущество только в случае, когда аргумент commute в MPI_OP_CREATE есть true. Также можно уменьшить размер временного буфера и конвейеризовать обмен с вычислением, пересылая и вычисляя редукцию в порциях размера len < count.

Предопределенные операции редукции могут быть реализованы как библиотека определенных пользователем операций. Однако, будет эффективнее, если MPI_REDUCE будет выполнять эти функции как частный случай.[]

Синтаксис функции MPI_OP_FREE(op) представлен ниже.

MPI_OP_FREE(op)

INOUT op операция (дескриптор)

int MPI_op_free(MPI_Op *op)

MPI_OP_FREE(OP, IERROR)
INTEGER OP, IERROR

void MPI::Op::Free()

Функция MPI_OP_FREE маркирует определенную пользователем операцию редукции для удаления и устанавливает значение MPI_OP_NULL для аргумента op.

Пример 4.20 Вычислить произведение массива комплексных чисел, язык Си. Используется определенная пользователем операция.

typedef struct { double real,imag; } Complex; /* функция, опредленная пользователем */ void myProd(Complex *in, Complex *inout, int *len, MPI_Datatype *dptr) { int i; Complex c; for (i=0; i< *len; ++i) { c.real = inout->real*in->real - inout->imag*in->imag; c.imag = inout->real*in->imag + inout->imag*in->real; *inout = c; in++; inout++; } } ... /* каждый процесс содержит массив из 100 комплексных чисел */ Complex a[100], answer[100]; MPI_Op myOp; MPI_Datatype ctype; /* объяснение для MPI, как определяется тип Complex */ MPI_Type_contiguous(2, MPI_DOUBLE, &ctype); MPI_Type_commit(&ctype); /* создается комплексная операция complex-product */ MPI_Op_create(myProd, True, &myOp); MPI_Reduce(a, answer, 100, ctype, myOp, root, comm); /* в этой точке результат из 100 Complexes, * помещается на корневой процесс */


next up previous contents
Next: Функция All-Reduce Up: Глобальные операции редукции Previous: MINLOС и MAXLOС   Contents
Alex Otwagin 2002-12-10