Программирование для многопроцессорных систем в стандарте MPI - Шпаковский Г.И., Серикова Н.В.. Программирование для многопроцессорных систем в стандарте MPI -. Организация вычислений в многопроцессорных системах
Скачать 1.61 Mb.
|
send является нелокальным. Синхронная неблоки- рующая передача может быть завершена, если перед вызовом receive имеет место соответствующий неблокирующий прием. Если исполь- зуется режим буферизуемой передачи, то сообщение должно быть бу- феризовано, если не имеется ждущего приема. В этом случае вызов send является локальным и обязан быть успешным независимо от со- стояния соответствующего приема. Если используется стандартный режим передачи, тогда вызов send может заканчиваться перед тем, как имеет место соответствующий прием, если сообщение буферизован- ное. С другой стороны, send может не завершаться до тех пор, пока имеет место соответствующий прием и сообщение было скопировано в приемный буфер. Неблокирующие передачи могут соответствовать блокирующим приемам и наоборот. 3.7.1. Коммуникационные объекты Неблокирующие обмены используют скрытые запросы , чтобы идентифицировать операции обмена и сопоставить операцию, которая инициирует обмен с операцией, которая заканчивает его. Они являют- ся системными объектами, которые становятся доступными в процес- се обработки. Объект запроса указывает различные свойства операции обмена, такие как режим передачи, связанный с ней буфер обмена, ее контекст, тэг и номер процесса-приемника, которые используются для посылки сообщения, или тэг и номер процесса-отправителя, которые используются для приема. В дополнение этот объект хранит инфор- мацию о состоянии ждущих операций обмена. 65 3.7.2. Инициация обмена Далее используются те же обозначения, что и для блокирующего обмена: преффикс B, S или R используются для буферизованного, синхронного режима или для режима готовности, преффикс I – для неблокирующего обмена. MPI_ISEND(buf, count, datatype, dest, tag, comm, request) IN buf начальный адрес буфера посылки (альтернатива) IN count число элементов в буфере посылки (целое) IN datatype тип каждого элемента в буфере посылки (дескриптор) IN dest номер процесса-получателя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (дескриптор) OUT request запрос обмена (дескриптор) int MPI_Isend (void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) MPI_ISEND (BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR) INTEGER COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR MPI::Request Comm::Isend(const void* buf, int count, const MPI::Datatype& datatype, int dest, int tag) const MPI_IBSEND(buf, count, datatype, dest, tag, comm, request) IN buf начальный адрес буфера посылки (альтернатива) IN count число элементов в буфере посылки (целое) IN datatype тип каждого элемента в буфере посылки (дескриптор) IN dest номер процесса-получателя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (дескриптор) OUT request запрос обмена (дескриптор) int MPI_Ibsend (void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) MPI_IBSEND (BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR) INTEGER COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR MPI::Request MPI::Comm::Ibsend(const void* buf, int count, const MPI::Datatype& datatype, int dest, int tag) const 66 MPI_ISSEND (buf, count, datatype, dest, tag, comm, request) IN buf начальный адрес буфера посылки (альтернатива) IN count число элементов в буфере посылки (целое) IN datatype тип каждого элемента в буфере посылки (дескриптор) IN dest номер процесса-получателя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (дескриптор) OUT request запрос обмена (дескриптор) int MPI_Issend (void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) MPI_ISSEND (BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR) INTEGER COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR MPI::Request MPI::Comm::Issend(const void* buf, int count, const MPI::Datatype& datatype, int dest, int tag) const MPI_IRSEND (buf, count, datatype, dest, tag, comm, request) IN buf начальный адрес буфера посылки (альтернатива) IN count число элементов в буфере посылки (целое) IN datatype тип каждого элемента в буфере посылки (дескриптор) IN dest номер процесса-получателя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (дескриптор) OUT request запрос обмена (дескриптор) int MPI_Irsend (void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) MPI_IRSEND (BUF, COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR) INTEGER COUNT, DATATYPE, DEST, TAG, COMM, REQUEST, IERROR MPI::Request MPI::Comm::Irsend(const void* buf, int count, const MPI::Datatype& datatype, int dest, int tag) const MPI_IRECV(buf, count, datatype, source, tag, comm, request) IN buf начальный адрес буфера посылки (альтернатива) IN count число элементов в буфере посылки (целое) IN source тип каждого элемента в буфере посылки (дескриптор) IN dest номер процесса-получателя (целое) IN tag тэг сообщения (целое) IN comm коммуникатор (дескриптор) OUT request запрос обмена (дескриптор) 67 int MPI_Irecv (void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Request *request) MPI_IRECV (BUF, COUNT, DATATYPE, SOURCE, TAG, COMM, REQUEST, IERROR) INTEGER COUNT, DATATYPE, SOURCE, TAG, COMM, REQUEST, IERROR MPI::Request MPI::Comm::Irecv(void* buf, int count, const MPI::Datatype& datatype, int source, int tag) const Эти вызовы создают объект коммуникационного запроса и связы- вают его с дескриптором запроса (аргумент request ). Запрос может быть использован позже, чтобы узнать статус обмена или чтобы ждать его завершения. Неблокирующий вызов посылки указывает, что система может стартовать, копируя данные из буфера отправителя. Отправитель не должен обращаться к любой части буфера посылки после того, как вызвана операция неблокируемой передачи, пока посылка не завер- шится. Неблокирующий прием указывает, что система может старто- вать, записывая данные в приемный буфер. Приемник не должен об- ращаться в любую часть приемного буфера после того, как вызвана операция неблокируемого приема, пока прием не завершен. 3.7.3. Завершение обмена Чтобы завершить неблокирующий обмен, используются функции MPI_WAIT и MPI_TEST . Завершение операции посылки указывает, что отправитель теперь может изменять содержимое ячеек буфера по- сылки (операция посылки сама не меняет содержание буфера). Опера- ция завершения не извещает, что сообщение было получено, но дает сведения, что оно было буферизовано коммуникационной подсисте- мой. Однако, если был использован синхронный режим, завершение операции посылки указывает, что соответствующий прием был ини- циирован и что это сообщение будет в конечном итоге принято этим соответствующим получателем. Завершение операции приема указывает, что приемный буфер со- держит принятое сообщение, что процесс-получатель теперь может обращаться к нему и что статусный объект установлен. Это не означа- ет, что операция посылки завершена. Нулевой дескриптор имеет значение MPI_REQUEST_NULL . Де- скриптор является активным, если он не является нулевым или неак- 68 тивным. Состояние empty (пусто) возвращает tag = MPI_ANY_TAG, error = MPI_SUCCESS, source = MPI_ANY_SOURCE , вызов MPI_GET_ELEMENTS и MPI_TEST_CANCELLED возвращают false , а вызов MPI_GET_COUNT возвращает count = 0 . Переменная состояния устанавливается на empty , когда возвращаемое ею значение несущественно. Состояние устанавливается таким образом, чтобы предупредить ошибки из-за устаревшей информации. MPI_WAIT (request, status) INOUT request запрос (дескриптор) OUT status объект состояния (статус) int MPI_Wait (MPI_Request *request, MPI_Status *status) MPI_WAIT(REQUEST, STATUS, IERROR) INTEGER REQUEST, STATUS(MPI_STATUS_SIZE), IERROR void MPI::Request::Wait (MPI::Status& status) Обращение к MPI_WAIT заканчивается, когда завершена опера- ция, указанная в запросе. Если коммуникационный объект, связанный с этим запросом, был создан вызовом неблокирующей посылки или приема, тогда этот объект удаляется при обращении к MPI_WAIT , и дескриптор запроса устанавливается в MPI_REQUEST_NULL. MPI_WAIT является нелокальной операцией. Вызов возвращает в status информацию о завершенной операции. Содержание статусного объекта для приемной операции может быть получено, как описано в параграфе 3.2.5. Разрешается вызывать MPI_WAIT с нулевым или неактивным ар- гументом запроса. В этом случае операция заканчивается немедленно со статусом empty MPI_TEST (request, flag, status) INOUT request коммуникационный запрос (дескриптор) OUT flag true, если операция завершена (логический тип) OUT status статусный объект (статус) int MPI_Test (MPI_Request *request, int *flag, MPI_Status *status) MPI_TEST(REQUEST, FLAG, STATUS, IERROR) LOGICAL FLAG INTEGER REQUEST, STATUS(MPI_STATUS_SIZE), IERROR bool MPI::Request::Test (MPI::Status& status) 69 Обращение к MPI_TEST возвращает flag = true , если операция, указанная в запросе, завершена. В таком случае статусный объект со- держит информацию о завершенной операции; если коммуникацион- ный объект был создан неблокирующей посылкой или приемом, то он входит в состояние дедлока, и обработка запроса устанавливается в MPI_REQUEST_NULL . Другими словами, вызов возвращает flag = false . В этом случае значение статуса не определено. MPI_TEST яв- ляется локальной операцией. Возвращенный статус для операции приема несет информацию, которая может быть получена, как описано в параграфе 3.2.5. Статус- ный объект для операции посылки несет информацию, которая может быть получена обращением к MPI_TEST_CANCELLED (параграф 3.8). Можно вызывать MPI_TEST с нулевым или неактивным аргу- ментом запроса. В таком случае операция возвращает flag = true и empty для status Функции MPI_WAIT и MPI_TEST могут быть использованы как для завершения, так и для приема. Пример 3.10. Простое использование неблокируемой операции. CALL MPI_COMM_RANK(comm, rank, ierr) IF(rank.EQ.0) THEN CALL MPI_ISEND(a(1), 10, MPI_REAL, 1, tag, comm, request, ierr) ! выполним вычисления до завершения операции посылки CALL MPI_WAIT(request, status, ierr) ELSE CALL MPI_IRECV(a(1), 15, MPI_REAL, 0, tag, comm, request, ierr) ! выполним вычисления до завершения операции приема CALL MPI_WAIT(request, status, ierr) END IF 3.7.4. Семантика неблокирующих коммуникаций Очередность . Операции неблокирующих коммуникаций упорядо- чены согласно порядку исполнения вызовов, которые инициируют обмен. Требование отсутствия обгона, описанного в параграфе 3.5, расширено на неблокирующий обмен. Пример 3.11. Установление очереди для неблокирующих операций. CALL MPI_COMM_RANK(comm, rank, ierr) IF (RANK.EQ.0) THEN CALL MPI_ISEND(a, 1, MPI_REAL, 1, 0, comm, r1, ierr) 70 CALL MPI_ISEND(b, 1, MPI_REAL, 1, 0, comm, r2, ierr) ELSE CALL MPI_IRECV(a,1,MPI_REAL,0,MPI_ANY_TAG, comm, r1, ierr) CALL MPI_IRECV(b, 1, MPI_REAL, 0, 0, comm, r2, ierr) END IF CALL MPI_WAIT(r1,status) CALL MPI_WAIT(r2,status) Первая посылка процесса с номером 0 будет соответствовать пер- вому приему процесса с номером 1, даже если оба сообщения посланы до того, как процесс с номером 1 выполнит тот или другой прием. Продвижение обмена. Вызов MPI_WAIT , который завершает прием, будет в конечном итоге заканчиваться, если соответствующая посылка была начата и не закрыта другим приемом. Если соответст- вующая посылка неблокирующая, тогда прием должен завершиться, даже если отправитель не выполняет никакого вызова, чтобы завер- шить передачу. Аналогично, обращение к MPI_WAIT , которое за- вершает посылку, будет заканчиваться, если соответствующий прием инициирован. Пример 3.12. Иллюстрация семантики продвижения. CALL MPI_COMM_RANK(comm, rank, ierr) IF (RANK.EQ.0) THEN CALL MPI_SSEND(a, 1, MPI_REAL, 1, 0, comm, ierr) CALL MPI_SEND(b, 1, MPI_REAL, 1, 1, comm, ierr) ELSE CALL MPI_IRECV(a, 1, MPI_REAL, 0, 0, comm, r, ierr) CALL MPI_RECV(b, 1, MPI_REAL, 0, 1, comm, ierr) CALL MPI_WAIT(r, status, ierr) END IF Код не имеет дедлока. Первая синхронная посылка процесса с номером 0 обязана завершиться после того, как процесс с номером 1 установит соответствующий (неблокирующий) прием, даже если про- цесс 1 не достиг еще завершения вызова wait. Поэтому процесс с но- мером 0 будет продолжаться и выполнит вторую посылку, позволяя процессу 1 завершить передачу. Если MPI_TEST , который завершает прием, вызывается повторно с тем же аргументом и соответствующая посылка стартовала, тогда вызов рано или поздно возвратит flag = true, если посылка не закрыта другим приемом. Если MPI_TEST , который завершает посылку, по- 71 вторяется с тем же аргументом и соответствующий прием стартовал, тогда вызов рано или поздно возвратит flag = true , если не будет за- крыт другой посылкой. 3.7.5. Множественные завершения Удобно иметь возможность ожидать завершения любой или всех операций в списке, а не ждать только специального сообщения. Вызо- вы MPI_WAITANY или MPI_TESTANY можно использовать для ожидания завершения одной из нескольких операций. Вызовы MPI_WAITALL или MPI_TESTALL могут быть использованы для всех ждущих операций в списке. Вызовы WAITSOME или MPI_TESTSOME можно использовать для завершения всех разре- шенных операций в списке. MPI_WAITANY (count, array_of_requests, index, status) IN count длина списка (целое) INOUT array_of_requests массив запросов (массив дескрипторов) OUT index индекс дескриптора для завершенной операции (целое) OUT status статусный объект (статус) int MPI_Waitany (int count, MPI_Request *array_of_requests, int *index, MPI_Status *status) MPI_WAITANY(COUNT, ARRAY_OF_REQUESTS, INDEX, STATUS, IERROR) INTEGER COUNT, ARRAY_OF_REQUESTS(*), INDEX, STATUS(MPI_STATUS_SIZE), IERROR static int MPI::Request::Waitany(int count, MPI::Request array_of_requests[], MPI::Status& status) Операция блокирует работу до тех пор, пока не завершится одна из операций из массива активных запросов. Если более чем одна опе- рация задействована и может закончиться, выполняется произвольный выбор. Операция возвращает в index индекс этого запроса в массиве и возвращает в status статус завершаемого обмена. Если запрос был создан операцией неблокирующего обмена, то он удаляется, и деск- риптор запроса устанавливается в MPI_REQUEST_NULL Список array_of_request может содержать нуль или неактивные дескрипторы. Если список не содержит активных дескрипторов (спи- сок имеет нулевую длину или все элементы являются нулями или не- активны), тогда вызов заканчивается немедленно с index = MPI_UNDEFINED и со статусом empty 72 Выполнение MPI_WAITANY (count, array_of_requests, index, status ) имеет тот же эффект, что и выполнение MPI_WAIT (&array_of_requests[i], status) , где i есть значение, возвращенное в аргументе index (если значение index не MPI_UNDEFINED ). MPI_WAITANY с массивом, содержащим один активный элемент, эквивалентно MPI _ WAIT Функция MPI_TESTANY тестирует завершение либо одной либо никакой из операций, связанных с активными дескрипторами. В пер- вом случае она возвращает flag = true , индекс этого запроса в массиве index и статус этой операции в status ; если запрос был создан вызо- вом неблокирующего обмена, то запрос удаляется, и дескриптор уста- навливается в MPI_REQUEST_NULL . Массив индексируется от нуля в языке Си и от единицы в языке Fortran. В последнем случае (не за- вершено никакой операции) возвращается flag = false , значение MPI_UNDEFINED в index и состояние аргумента status является не- определенным. Массив может содержать нуль или неактивные деск- рипторы. Если массив не содержит активных дескрипторов, то вызов заканчивается немедленно с flag = true, index = MPI_UNDEFINED и status = empty . Если массив запросов содержит активные дескрипто- ры, тогда выполнение MPI_TESTANY (count, array_of_requests, index, status) имеет тот же эффект, как и выполнение MPI_TEST (&array_of_requests[i], flag, status) для i = 0, 1 ,..., count-1 в некото- ром произвольном порядке, пока один вызов не возвратит flag = true , или все вызовы не могут быть выполнены. В первом случае индекс устанавливается на последнее значение |