Прием/передача сообщений между отдельными процессами
Все функции передачи сообщений в MPI делятся на две группы. В одну группу входят функции, которые предназначены для взаимодействия двух процессов программы. Такие операции называются индивидуальными или операциями типа точка-точка. Функции другой группы предполагают, что в операцию должны быть вовлечены все процессы некоторого коммуникатора. Такие операции называются коллективными. Начнем описание функций обмена сообщениями с обсуждения операций типа точка-точка. Все функции данной группы, в свою очередь, так же делятся на два класса: функции с блокировкой (с синхронизацией) и функции без блокировки (асинхронные).
Прием/передача сообщений с блокировкой задаются конструкциями следующего вида. int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm) • buf — адрес начала буфера с посылаемым сообщением; • count — число передаваемых элементов в сообщении; • datatype — тип передаваемых элементов; • dest — номер процесса-получателя; • msgtag — идентификатор сообщения; • comm — идентификатор коммуникатора. Блокирующая посылка сообщения с идентификатором msgtag, состоящего из count элементов типа datatype, процессу с номером dest. Все элементы посылаемого сообщения расположены подряд в буфере buf. Значение count может быть нулем. Разрешается передавать сообщение самому себе. Тип передаваемых элементов datatype должен указываться с помощью предопределенных констант типа, например, mpi_int, mpi_long, mpi_short, mpi_long_double, mpi_char, mpi_unsigned_char, mpi_float и т.п. Для каждого типа данных языков Фортран и Си есть своя константа. Полный список предопределенных имен типов можно найти в файле mpi.h.
Блокировка гарантирует корректность повторного использования всех параметров после возврата из подпрограммы. Это означает, что после возврата из данной функции можно использовать любые присутствующие в вызове функции переменные без опасения испортить передаваемое сообщение. Выбор способа осуществления этой гарантии: копирование в промежуточный буфер или непосредственная передача процессу dest, остается за разработчиками конкретной реализации MPI.
Следует специально отметить, что возврат из функции MPI_Send не означает ни того, что сообщение получено процессом dest, ни того, что сообщение покинуло процессорный элемент, на котором выполняется процесс, выполнивший MPI_Send. Предоставляется только гарантия безопасного изменения переменных, использованных в вызове данной функции. Подобная неопределенность далеко не всегда устраивает пользователя. Чтобы расширить возможности передачи сообщений, в MPI введены дополнительные три функции. Все параметры у этих функций такие же, как и у функции MPI_Send, однако у каждой из них есть своя особенность.
MPI_Bsend — передача сообщения с буферизацией. Если прием посылаемого сообщения еще не был инициализирован процессом-получателем, то сообщение будет записано в буфер, и произойдет немедленный возврат из функции. Выполнение данной функции никак не зависит от соответствующего вызова функции приема сообщения. Тем не менее, функция может вернуть код ошибки, если места под буфер недостаточно.
MPI_Ssend — передача сообщения с синхронизацией. Выход из данной функции произойдет только тогда, когда прием посылаемого сообщения будет инициализирован процессом-получателем. Таким образом, завершение передачи с синхронизацией говорит не только о возможности повторного использования буфера, но и о гарантированном достижении процессом-получателем точки приема сообщения в программе. Если прием сообщения так же выполняется с блокировкой, то функция MPI_Ssend сохраняет семантику блокирующих вызовов.
MPI_Rsend — передача сообщения по готовности. Данной функцией можно пользоваться только в том случае, если процесс-получатель уже инициировал прием сообщения. В противном случае вызов функции, вообще говоря, является ошибочным и результат ее выполнения не определен. Во многих реализациях функция MPI_Rsend сокращает протокол взаимодействия между отправителем и получателем, уменьшая накладные расходы на организацию передачи.
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int msgtag, MPI_Comm comm, MPI_Status *status) • out buf — адрес начала буфера для приема сообщения; • count — максимальное число элементов в принимаемом сообщении; • datatype — тип элементов принимаемого сообщения; • source — номер процесса- отправителя; • msgtag — идентификатор принимаемого сообщения; • comm — идентификатор коммуникатора; • out status — параметры принятого сообщения. Прием сообщения с идентификатором msgtag от процесса source с блокировкой. Число элементов в принимаемом сообщении не должно превосходить значения count. Если число принятых элементов меньше значения count, то гарантируется, что в буфере buf изменятся только элементы, соответствующие элементам принятого сообщения. Если нужно узнать точное число элементов в принимаемом сообщении, то можно воспользоваться функцией MPI_Get_count. Блокировка гарантирует, что после возврата из функции все элементы сообщения уже будут приняты и расположены в буфере buf. Ниже приведен пример программы, в которой каждый процесс выводит на консоль «Здравствуй MPI мир! Я “номер процесса – rank” из “количество запущенных процессов – size” ». Процессы после их порождения функцией MPI_Init завершатся, выполнив функцию MPI_Finalize.
#include<mpi.h> #include<stdio.h> int main(int argc, char** argv) { int rank, size; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD,&rank); MPI_Comm_size(MPI_COMM_WORLD,&size); printf("Hello, MPI world! I am %d of %d\n",rank,size); MPI_Finalize(); return 0; }
Ниже приведен пример программы, в которой если будет запущено процессов числом не равным 2, на экран каждый из процессов выдаст сообщение о том, сколько запущено процессов. Если число процессов равно 2м, то Первый процесс выдаст сообщение на консоль, что он имеет номер 1 из 2х, а также выведет информацию об workmate.
#include <mpi.h> #include <stdio.h> #include <unistd.h> int main(int argc, char** argv) { int rank,size; char hostname[100]; MPI_Status st; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); if (size!=2) { printf("This example should be run on 2 processors, now there are %f\n", size); MPI_Finalize(); return 0; } else { MPI_Comm_rank(MPI_COMM_WORLD,&rank); if(rank==0) { gethostname(hostname, 100); MPI_Send(hostname, 100, MPI_CHAR, 1, 99, MPI_COMM_WORLD); } else { MPI_Recv(hostname, 100, MPI_CHAR, 0, 99, MPI_COMM_WORLD,&st); printf("I am %d of %d. My workmate is %s\n", rank,size,hostname); } MPI_Finalize(); return 0; } }
Ниже приведен пример программы, в которой нулевой процесс посылает сообщение процессу с номером один и ждет от него ответа. Если программа будет запущена с большим числом процессов, то реально выполнять пересылки все равно станут только нулевой и первый процессы. Остальные процессы после их порождения функцией MPI_Init сразу завершатся, выполнив функцию MPI_Finalize. #include "mpi.h" #include <stdio.h> int main(int argc, char **argv) { int numtasks, rank, dest, src, rc, tag=1; char inmsg, outmsg='x'; MPI_Status Stat; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD, &numtasks); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0) { dest = 1; src = 1; rc = MPI_Send(&outmsg, 1, MPI_CHAR, dest, tag, MPI_COMM_WORLD); rc = MPI_Recv(&inmsg, 1, MPI_CHAR, src, tag, MPI_COMM_WORLD, &Stat); } Else if (rank == 1) { dest = 0; src = 0; rc = MPI_Recv(&inmsg, 1, MPI_CHAR, src, tag, MPI_COMM_WORLD, &Stat); rc = MPI_Send(&outmsg, 1, MPI_CHAR, dest, tag, MPI_COMM_WORLD); } MPI_Finalize(); } В следующем примере каждый процесс с четным номером посылает сообщение своему соседу с номером на единицу большим. Дополнительно поставлена проверка для процесса с максимальным номером, чтобы он не послал сообщение несуществующему процессу. Показана только схема программы. #include "mpi.h" #include <stdio.h> main(int argc, char **argv) { int me, size; int SOME_TAG=0; MPI_Status status; … MPI_Init (&argc, &argv); MPI_Comm_rank (MPI_COMM_WORLD, &me); MPI_Comm_size (MPI_COMM_WORLD, &size); f ((me % 2) == 0) { if ((me+1) < size /*посылают все процессы кроме последнего*/ MPI_Send (…, me+1, SOME_TAG, MPI_COMM_WORLD); } Else MPI_Recv (…, me-1, SOME_TAG, MPI_COMM_WORLD, &status); … MPI_Finalize(); } В качестве номера процесса-отправителя можно указать предопределенную константу mpi_any_source — признак того, что подходит сообщение от любого процесса. В качестве идентификатора принимаемого сообщения можно указать константу mpi_any_tag — это признак того, что подходит сообщение с любым идентификатором. При одновременном использовании этих двух констант будет принято любое сообщение от любого процесса.
Параметры принятого сообщения всегда можно определить по соответствующим полям структуры status. Предопределенный тип MPi_Status описан в файле mpi-h. В языке Си параметр status является структурой, содержащей поля с именами mpi_source, mpi_tag и mpi_error. Реальные значения номера процесса-отправителя, идентификатора сообщения и кода ошибки доступны через status.MPiSOURCE, status.MPiTAG и status.MPiERROR. В Фортране параметр status является целочисленным массивом размера mpi_status_size. Константы mpi_source, mpi_tag и mpi_error являются индексами по данному массиву для доступа к значениям соответствующих полей, например status(MPI_SOURCE).
Обратим внимание на некоторую несимметричность операций посылки и приема сообщений. С помощью константы _MPI_ANY_SOURCE_можно принять сообщение от любого процесса. Однако в случае посылки данных требуется явно указать номер принимающего процесса.
В стандарте оговорено, что если один процесс последовательно посылает два сообщения другому процессу, и оба эти сообщения соответствуют одному и тому же вызову MPi_Recv, то первым будет принято сообщение, которое было отправлено раньше. Вместе с тем, если два сообщения были одновременно отправлены разными процессами, и оба сообщения соответствуют одному и тому же вызову MPi_Recv, то порядок их получения принимающим процессом заранее не определен. Последнее замечание относительно использования блокирующих функций приема и посылки связано с возможным возникновением тупиковой ситуации. Предположим, что работают два параллельных процесса и они хотят обменяться данными. Было бы вполне естественно в каждом процессе сначала воспользоваться функцией MPi_Send, а затем MPi_Recv. Но именно этого и не стоит делать. Дело в том, что мы заранее не знаем, как реализована функция MPi_Send. Если разработчики для гарантии корректного повторного использования буфера посылки заложили схему, при которой посылающий процесс ждет начала приема, то возникнет классический тупик. Первый процесс не может вернуться из функции посылки, поскольку второй не начинает прием сообщения. А второй процесс не может начать прием сообщения, поскольку сам по похожей причине застрял на посылке. Выход из этой ситуации прост. Нужно использовать либо неблокирующие функции приема/передачи, либо функцию совмещенной передачи и приема. Оба варианта мы обсудим ниже.
int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count) • status — параметры принятого сообщения; • datatype — тип элементов принятого сообщения; • OUT count — число элементов сообщения. По значению параметра status данная функция определяет число уже принятых (после обращения к MPI_Recv) или принимаемых (после обращения к MPI_Probe или MPI_Iprobe) элементов сообщения типа datatype. Данная функция, в частности, необходима для определения размера области памяти, выделяемой для хранения принимаемого сообщения.
int MPI_Probe(int source, int msgtag, MPI_Comm comm, MPI_Status *status) • source — номер процесса-отправителя или mpi_any_source; • msgtag — идентификатор ожидаемого сообщения или mpi_any_tag; • comm — идентификатор коммуникатора; • out status — параметры найденного подходящего сообщения. Получение информации о структуре ожидаемого сообщения с блокировкой. Возврата из функции не произойдет до тех пор, пока сообщение с подходящим идентификатором и номером процесса-отправителя не будет доступно для получения. Атрибуты доступного сообщения можно определить обычным образом с помощью параметра status. Следует особо обратить внимание на то, что функция определяет только факт прихода сообщения, но реально его не принимает. Прием/передача сообщений без блокировки. В отличие от функций с блокировкой, возврат из функций данной группы происходит сразу без какой-либо блокировки процессов. На фоне дальнейшего выполнения программы одновременно происходит и обработка асинхронно запущенной операции. В принципе, данная возможность исключительно полезна для создания эффективных программ. В самом деле, программист знает, что в некоторый момент ему потребуется массив, который вычисляет другой процесс. Он заранее выставляет в программе асинхронный запрос на получение данного массива, а до того момента, когда массив реально потребуется, он может выполнять любую другую полезную работу. Опять же, во многих случаях совершенно не обязательно дожидаться окончания посылки сообщения для выполнения последующих вычислений.
Если есть возможность операции приема/передачи сообщений скрыть на фоне вычислений, то этим, вроде бы, надо безоговорочно пользоваться. Однако на практике не все согласуется с теорией. Многое зависит от конкретной реализации. К сожалению, далеко не всегда асинхронные операции эффективно поддерживаются аппаратурой и системным окружением. Поэтому не стоит удивляться, если эффект от выполнения вычислений на фоне пересылок окажется нулевым. Сделанные замечания касаются только вопросов эффективности. В отношении предоставляемой функциональности асинхронные операции исключительно полезны, поэтому они присутствуют практически в каждой реальной программе. int MPI_Isend(void *buf, int count, MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm, MPI_Request *request) • buf — адрес начала буфера c посылаемым сообщением; • count — число передаваемых элементов в сообщении; • datatype — тип передаваемых элементов; • dest — номер процесса-получателя; • msgtag — идентификатор сообщения; • comm — идентификатор коммуникатора, • OUT request — идентификатор асинхронной операции. Передача сообщения аналогична вызову MPI_Send, однако возврат из функции MPI_Isend происходит сразу после инициализации процесса передачи без ожидания обработки всего сообщения, находящегося в буфере buf. Это означает, что нельзя повторно использовать данный буфер для других целей без получения дополнительной информации, подтверждающей завершение данной посылки. Определить тот момент времени, когда можно повторно использовать буфер buf без опасения испортить передаваемое сообщение, можно с помощью параметра request и функций MPI_Wait и MPI_Test. Аналогично трем модификациям функции MPI_Send, предусмотрены три дополнительных варианта функций MPI_Ibsend, MPI_Issend, MPI_Irsend. К изложенной выше семантике работы этих функций добавляется асинхронность.
int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int source, int msgtag, MPI_Comm comm, MPI_Request *request) • out buf — адрес начала буфера для приема сообщения; • count — максимальное число элементов в принимаемом сообщении; • datatype — тип элементов принимаемого сообщения; • source — номер процесса- отправителя; • msgtag — идентификатор принимаемого сообщения; • comm — идентификатор коммуникатора; • out request — идентификатор операции асинхронного приема сообщения. Прием сообщения, аналогичный MPI_Recv, однако возврат из функции происходит сразу после инициализации процесса приема без ожидания получения всего сообщения и его записи в буфере buf. Окончание процесса приема можно определить с помощью параметра request и процедур типа MPI_Wait и MPITest.
Сообщение, отправленное любой из функций MPI_Send, MPI_Isend и любой из трех их модификаций, может быть принято любой из процедур MPI_Recv и MPI_Irecv. С помощью данной функции легко обойти возможную тупиковую ситуацию, о которой говорилось ранее. Заменим вызов функции приема сообщения с блокировкой на вызов функции MPI_Irecv. Расположим его перед вызовом функции MPI_Send, т.е. преобразуем фрагмент следующим образом: В такой ситуации тупик гарантированно не возникнет, поскольку к моменту вызова функции MPI_Send запрос на прием сообщения уже будет выставлен.
int MPI_Wait(MPI_Request *request, MPI_Status *status) • request— идентификатор операции асинхронного приема или передачи; • out status — параметры сообщения. Ожидание завершения асинхронной операции, ассоциированной с идентификатором request и запущенной функцией MPI_Isend или MPI_Irecv. Пока асинхронная операция не будет завершена, процесс, выполнивший функцию MPi_Wait, будет заблокирован. Если речь идет о приеме, атрибуты и длину принятого сообщения можно определить обычным образом с помощью параметра status.
int MPI_Waitall(int count, MPIRequest *requests, MPIStatus *statuses) • count — число идентификаторов асинхронных операций; • requests — идентификаторы операций асинхронного приема или передачи; • OUT statuses — параметры сообщений. Выполнение процесса блокируется до тех пор, пока все операции обмена, ассоциированные с указанными идентификаторами, не будут завершены. Если во время одной или нескольких операций обмена возникли ошибки, то поле ошибки в элементах массива statuses будет установлено в соответствующее значение.
Ниже показан пример программы, в которой все процессы обмениваются сообщениями с ближайшими соседями в соответствии с топологией кольца.
#include "mpi.h" #include <stdio.h> int main(int argc, char **argv) { int numtasks, rank, next, prev, buf[2], tag1=1, tag2=2; MPI_Request reqs[4]; MPI_Status stats[4]; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD, &numtasks); MPI_Comm_rank(MPI_COMM_WORLD, &rank); prev = rank – 1; next = rank + 1; if (rank == 0) prev = numtasks – 1; if (rank == (numtasks – 1)) next = 0; MPI_Irecv(&buf[0], 1, MPI_INT, prev, tag1, MPI_COMM_WORLD,&reqs[0]); MPI_Irecv(&buf[1], 1, MPI_INT, next, tag2, MPI_COMM_WORLD,&reqs[1]); MPI_Isend(&rank, 1, MPI_INT, prev, tag2, MPI_COMM_WORLD,&reqs[2]); MPI_Isend(&rank, 1, MPI_INT, next, tag1, MPI_COMM_WORLD,&reqs[3]); MPI_Waitall(4, reqs, stats); MPI_Finalize(); }
int MPI_Waitany( int count, MPI_Request *requests, int *index, MPI_Status *status) • count— число идентификаторов асинхронных операций; • requests — идентификаторы операций асинхронного приема или передачи; • outindex— номер завершенной операции обмена; • outstatus — параметры сообщений. Выполнение процесса блокируется до тех пор, пока какая-либо асинхронная операция обмена, ассоциированная с указанными идентификаторами, не будет завершена. Если завершились несколько операций, то случайным образом будет выбрана одна из них. Параметр index содержит номер элемента в массиве requests, содержащего идентификатор завершенной операции.
int MPI_Waitsome( int incount, MPI_Request *requests, int *outcount, int *indexes, MPI_Status *statuses) • incount— число идентификаторов асинхронных операций; • requests — идентификаторы операций асинхронного приема или передачи; • out outcount — число идентификаторов завершившихся операций обмена; • outindexes— массив номеров завершившихся операции обмена; • outstatuses— параметры завершившихся сообщений. Выполнение процесса блокируется до тех пор, пока хотя бы одна из операций обмена, ассоциированных с указанными идентификаторами, не будет завершена. Параметр outcount содержит число завершенных операций, а первые outcount элементов массива indexes содержат номера элементов массива requests с их идентификаторами. Первые outcount элементов массива statuses содержат параметры завершенных операций.
int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status) • request — идентификатор операции асинхронного приема или передачи; • out flag — признак завершенности операции обмена; • out status — параметры сообщения. Проверка завершенности асинхронных функций MPI_Isend или MPI_Irecv, ассоциированных с идентификатором request. В параметре flag функция MPI_Test возвращает значение 1, если соответствующая операция завершена, и значение 0 в противном случае. Если завершена процедура приема, то атрибуты и длину полученного сообщения можно определить обычным образом с помощью параметра status.
int MPI_Testall( int count, MPI_Request *requests, int *flag, MPI_Status *statuses) • count — число идентификаторов асинхронных операций; • requests — идентификаторы операций асинхронного приема или передачи; • out flag — признак завершенности операций обмена; • OUT statuses — параметры сообщений. В параметре flag функция возвращает значение 1, если все операции, ассоциированные с указанными идентификаторами, завершены. В этом случае параметры сообщений будут указаны в массиве statuses. Если какая-либо из операций не завершилась, то возвращается 0, и определенность элементов массива statuses не гарантируется.
int MPI_Testany(int count, MPI_Request *requests, int *index, int *flag, MPI_Status *status) • count — число идентификаторов асинхронных операций; • requests — идентификаторы операций асинхронного приема или передачи; • out index — номер завершенной операции обмена; • out flag — признак завершенности операции обмена; • out status — параметры сообщения. Если к моменту вызова функции MPI_Testany хотя бы одна из операций асинхронного обмена завершилась, то в параметре flag возвращается значение 1, index содержит номер соответствующего элемента в массиве requests, а status — параметры сообщения. В противном случае в параметре flag будет возвращено значение 0.
int MPI_Testsome( int incount, MPI_Request *requests, int *outcount, int *indexes, MPI_Status *statuses) • incount — число идентификаторов асинхронных операций; • requests — идентификаторы операций асинхронного приема или передачи; • out outcount — число идентификаторов завершившихся операций обмена; • out indexes — массив номеров завершившихся операции обмена; • out statuses — параметры завершившихся операций. Данная функция работает так же, как и MPi_Waitsome, за исключением того, что возврат происходит немедленно. Если ни одна из указанных операций не завершилась, то значение outcount будет равно нулю.
int MPI_Iprobe( int source, int msgtag, MPI_Comm comm, int *flag, MPIStatus *status) • source — номер процесса-отправителя или mpi_any_source; • msgtag — идентификатор ожидаемого сообщения или mpi_any_tag; • comm — идентификатор коммуникатора; • out flag — признак завершенности операции обмена; • out status — параметры подходящего сообщения. Получение информации о поступлении и структуре ожидаемого сообщения без блокировки. В параметре flag возвращается значение 1, если сообщение с подходящими атрибутами уже может быть принято (в этом случае ее действие полностью аналогично MPI_Probe), и значение 0, если сообщения с указанными атрибутами еще нет.
Объединение запросов на взаимодействие. Процедуры данной группы позволяют снизить накладные расходы, возникающие в рамках одного процессора при обработке приема/передачи и перемещении необходимой информации между процессом и сетевым контроллером. Несколько запросов на прием и/или передачу могут объединяться вместе для того, чтобы далее их можно было бы запустить одной командой. Способ приема сообщения никак не зависит от способа его посылки: сообщение, отправленное с помощью объединения запросов либо обычным способом, может быть принято как обычным способом, так и с помощью объединения запросов.
int MPI_Send_init( void *buf, int count, MPI_Datatype datatype, int dest, int msgtag, MPI_Comm comm, MPI_Request *request) • buf — адрес начала буфера с посылаемым сообщением; • count — число передаваемых элементов в сообщении; • datatype — тип передаваемых элементов; • dest — номер процесса-получателя; • msgtag — идентификатор сообщения; • comm — идентификатор коммуникатора; • out request — идентификатор асинхронной передачи. Формирование запроса на выполнение пересылки данных. Все параметры точно такие же, как и у подпрограммы MPI_Isend, однако в отличие от нее пересылка не начинается до вызова подпрограммы MPI_Startall. Как и прежде, дополнительно предусмотрены варианты и для трех модификаций посылки сообщений: MPI_Bsend_init, MPI_Ssend_init, MPI_Rsend_init.
int MPI_Recv_init( void *buf, int count, MPI_Datatype datatype, int source, int msgtag, MPI_Comm comm, MPI_Request *request) • out buf — адрес начала буфера приема сообщения; • count — число принимаемых элементов в сообщении; • datatype — тип принимаемых элементов; • source — номер процесса- отправителя; • msgtag — идентификатор сообщения; • comm — идентификатор коммуникатора; • out request — идентификатор асинхронного приема. Формирование запроса на выполнение приема сообщения. Все параметры точно такие же, как и у функции MPI_Irecv, однако в отличие от нее реальный прием не начинается до вызова функции MPI_Startall.
MPI_Startall(int count, MPI_Request *requests) • count— число запросов на взаимодействие; • outrequests— массив идентификаторов приема/передачи Запуск всех отложенных операций передачи и приема, ассоциированных с элементами массива запросов requests и инициированных функциями MPI_Recv_init, MPI_Send_init или ее тремя модификациями. Все отложенные взаимодействия запускаются в режиме без блокировки, а их завершение можно определить обычным образом с помощью функций семейств MPI_Wait и MPITest.
Совмещенные прием и передача сообщений. Совмещение приема и передачи сообщений между процессами позволяет легко обходить множество подводных камней, связанных с возможными тупиковыми ситуациями. Предположим, что в линейке процессов необходимо организовать обмен данными между i-м и (i+D-ым процессами. Если воспользоваться стандартными блокирующими функциями посылки сообщений, то возможен тупик, обсуждавшийся ранее. Один из способов обхода такой ситуации состоит в использовании функции совмещенного приема и передачи. int MPI_Sendrecv( void *sbuf, int scount, MPI_Datatype stype, int dest, int stag, void *rbuf, int rcount, MPI_Datatype rtype, int source, MPI_Datatype rtag, MPI_Comm comm, MPI_Status *status) • sbuf — адрес начала буфера с посылаемым сообщением; • scount — число передаваемых элементов в сообщении; • stype — тип передаваемых элементов; • dest — номер процесса-получателя; • stag — идентификатор посылаемого сообщения; • out rbuf — адрес начала буфера приема сообщения; • rcount — число принимаемых элементов сообщения; • rtype — тип принимаемых элементов; • source — номер процесса- отправителя; • rtag — идентификатор принимаемого сообщения; • comm — идентификатор коммуникатора; • out status — параметры принятого сообщения. Данная операция объединяет в едином запросе посылку и прием сообщений. Естественно, что реализация этой функции гарантирует отсутствие тупиков, которые могли бы возникнуть между процессами при использовании обычных блокирующих операций MPi_Send и MPi_Recv. Принимающий и отправляющий процессы могут являться одним и тем же процессом. Буфера приема и посылки обязательно должны быть различными. Сообщение, отправленное операцией MPiSendrecv, может быть принято обычным образом, и точно также операция MPi_Sendrecv может принять сообщение, отправленное обычной операцией MPiSend.
Популярное: Модели организации как закрытой, открытой, частично открытой системы: Закрытая система имеет жесткие фиксированные границы, ее действия относительно независимы... Как выбрать специалиста по управлению гостиницей: Понятно, что управление гостиницей невозможно без специальных знаний. Соответственно, важна квалификация... Как распознать напряжение: Говоря о мышечном напряжении, мы в первую очередь имеем в виду мускулы, прикрепленные к костям ... ©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (2804)
|
Почему 1285321 студент выбрали МегаОбучалку... Система поиска информации Мобильная версия сайта Удобная навигация Нет шокирующей рекламы |