next up previous contents
Next: Распределение и использование буферов Up: Парные межпроцессные обмены Previous: Коммуникационные режимы   Contents

Семантика парного обмена между процессами.

Правильная реализация MPI гарантирует определенные общие свойства парного обмена, которые описаны ниже.

Очередность (Оrder). Сообщения не обгоняют друг друга: если отправитель последовательно посылает два сообщения на один адрес, то они должны быть приняты в том же порядке. Если получатель инициирует два приема последовательно и оба соответствуют одному и тому же сообщению, то вторая операция не будет принимать это сообщение, если первая операция все еще не выполнена. Это требование облегчает установление соответствия посылки и приема. Оно гарантирует, что программа с передачей сообщений детерминирована, если процессы однопоточные и операция MPI_ANY_SOURCE не используется при приеме. (Некоторые вызовы, описанные позже, такие как MPI_CANCEL или MPI_WAITANY, являются дополнительным источником недетерминизма).

Если процесс имеет однопоточное исполнение, тогда любые два обмена, выполняемые этим процессом, упорядочены. С другой стороны, если процесс многопоточный, тогда семантика потокового исполнения может быть неопределена относительно порядка для двух операций посылки, выполняемых двумя различными ветвями. Операции логически являются конкурентами, даже если одна физически предшествует другой. Аналогично, если две операции приема, которые логически конкурентны, принимают два последовательно посланных сообщения, то два сообщения могут соответствовать двум приемам в различном порядке.

Пример 3.5 Пример необгоняющих сообщений

CALL MPI_COMM_RANK(comm, rank, ierr) IF (rank.EQ.0) THEN CALL MPI_BSEND(buf1, count, MPI_REAL, 1, tag, comm, ierr) CALL MPI_BSEND(buf2, count, MPI_REAL, 1, tag, comm, ierr) ELSE ! rank.EQ.1 CALL MPI_RECV(buf1, count, MPI_REAL, 0, MPI_ANY_TAG, comm, status, ierr) CALL MPI_RECV(buf2, count, MPI_REAL, 0, tag, comm, status, ierr) END IF

Сообщение, посланное в первой передаче, обязано быть принято в первом приеме; сообщение, посланное во второй передаче, обязано быть принято во втором приеме.

Продвижение обмена (Progress). Если пара соответствующих посылки и приема инициализирована двумя процессами, тогда по крайней мере одна из этих двух операций будет завершена, независимо от других действий в системе: операция посылки будет завершена, несмотря на то, что прием завершен другим сообщением; операция приема будет завершена, не глядя на то, что посланное сообщение будет поглощено другим соответствующим приемом, который был установлен на том же самом процессе-получателе.

Пример 3.6 Пример двух пересекающихся пар.

CALL MPI_COMM_RANK(comm, rank, ierr) IF (rank.EQ.0) THEN CALL MPI_BSEND(buf1, count, MPI_REAL, 1, tag1, comm, ierr) CALL MPI_SSEND(buf2, count, MPI_REAL, 1, tag2, comm, ierr) ELSE ! rank.EQ.1 CALL MPI_RECV(buf1, count, MPI_REAL, 0, tag2, comm, status, ierr) CALL MPI_RECV(buf2, count, MPI_REAL, 0, tag1, comm, status, ierr) END IF

Оба процесса начинают их первый коммуникационный вызов.Поскольку первая посылка процесса нуль использует буферизованный режим, она обязана завершиться безотносительно к состоянию процесса один. Поскольку никакого соответствующего приема не инициировано, сообщение будет скопировано в буферное пространство. (Если буферного пространства недостаточно, программа не может выполняться дальше). Затем начинается вторая посылка. На этот момент соответствующая пара операций приема и посылки запущена и обе операции обязаны завершиться. Процесс один затем вызывает свою вторую операцию приема, которая получит буферизованное сообщение. Заметим, что процесс один получает сообщения в порядке, обратном порядку их передачи.

Однозначность (Fairness). MPI не гарантирует однозначности выполнения коммуникаций. Предположим, что посылка инициирована. Тогда возможно, что процесс-получатель повторно инициирует прием, соответствующий этой посылке, хотя сообщение все еще не принято, поскольку оно всякий раз обгоняется другим сообщением, посланным другим источником. Аналогично, предположим, что прием был установлен многопоточным процессом. Тогда возможно, что сообщения, соответствующие этому приему, принимаются повторно, хотя прием все еще не закрыт, поскольку он обгоняется другими приемами, установленными на этом узле (другими выполняемыми ветвями.). Предупредить зависание в такой ситуации является обязанностью программиста.

Ограничение по ресурсам (Resource limitations). Любое выполнение операций обмена предполагает наличие ресурсов, которые, однако, могут быть ограниченными. Может возникнуть ошибка, когда недостаток ресурсов ограничивает выполнение вызова. Хорошая реализация MPI должна обеспечивать фиксированный объем ресурсов для каждой ждущей посылки в режиме готовности или синхронном режиме и для каждого ждущего приема. Однако, буферное пространство может быть израсходовано на хранение сообщений, посланных в стандартном режиме, или занято для хранения сообщений, посланных в буферном режиме, когда прием для соответствующей пары недоступен. В таких случаях объем пространства, доступного для буферизации, будет намного меньше, чем объем памяти для данных на многих системах.

MPI позволяет пользователю обеспечить буферную память для сообщений, посланных в буферизованном режиме. Более того, MPI описывает детализированную операционную модель для использования этого буфера. От реализации MPI требуется, чтобы она не ухудшила возможности, предоставляемые этой моделью. Это позволяет пользователям избегать переполнений буфера, когда они используют буферизованные передачи. Распределение и использование буферов описано в разделе 3.6.

Операции буферизованной передачи, которые не могут завершиться из-за недостатка буферного пространства, являются неверными. Когда такая ситуация выявлена, появляется сигнал об ошибке, и это может вызвать ненормальное окончание программы. С другой стороны, операция стандартной посылки, которая не может завершиться из-за недостатка буферного пространства, будет просто блокирована, ожидая, когда освободится буферное пространство или будет установлен соответствующий прием. Это поведение предпочтительно во многих ситуациях. Рассмотрим ситуацию, в которой поставщик многократно генерирует новые значения и посылает их потребителю. Предположим, что генерация производится быстрее, чем потребитель может принять их. Если используются буферизованные посылки, то результатом станет перегрузка буфера. Чтобы предупредить такую ситуацию, в программу необходимо ввести дополнительную синхронизацию. Если используются стандартные передачи, то производитель будет автоматически следовать за блокированием его операций из-за недостатка буферного пространства.

В некоторых ситуациях недостаток буферного пространства ведет к дедлоку. Это иллюстрируется примером ниже.

Пример 3.7 Обмен сообщениями.

CALL MPI_COMM_RANK(comm, rank, ierr) IF (rank.EQ.0) THEN CALL MPI_SEND(sendbuf, count, MPI_REAL, 1, tag, comm, ierr) CALL MPI_RECV(recvbuf, count, MPI_REAL, 1, tag, comm, status, ierr) ELSE ! rank.EQ.1 CALL MPI_RECV(recvbuf, count, MPI_REAL, 0, tag, comm, status, ierr) CALL MPI_SEND(sendbuf, count, MPI_REAL, 0, tag, comm, ierr) END IF

Эта программа будет успешной, даже если не хватит буферного пространства для данных. Операция стандартной передачи данных в этом примере может быть заменена синхронной передачей.

Пример 3.8 Попытка обмена сообщениями.

CALL MPI_COMM_RANK(comm, rank, ierr) IF (rank.EQ.0) THEN CALL MPI_RECV(recvbuf, count, MPI_REAL, 1, tag, comm, status, ierr) CALL MPI_SEND(sendbuf, count, MPI_REAL, 1, tag, comm, ierr) ELSE ! rank.EQ.1 CALL MPI_RECV(recvbuf, count, MPI_REAL, 0, tag, comm, status, ierr) CALL MPI_SEND(sendbuf, count, MPI_REAL, 0, tag, comm, ierr) END IF

Операция приема первого процесса обязана завершиться перед его посылкой и может завершиться только в том случае, если выполнена соответствующая посылка второго процесса. Операция приема второго процесса обязана завершиться перед его посылкой и может завершиться только тогда, когда выполнена соответствующая посылка первого процесса. Эта программа всегда будет находиться в состоянии взаимного блокирования. То же самое справедливо для любых других режимов передачи.

Пример 3.9 Обмен, который зависит от буферизации.

CALL MPI_COMM_RANK(comm, rank, ierr) IF (rank.EQ.0) THEN CALL MPI_SEND(sendbuf, count, MPI_REAL, 1, tag, comm, ierr) CALL MPI_RECV(recvbuf, count, MPI_REAL, 1, tag, comm, status, ierr) ELSE ! rank.EQ.1 CALL MPI_SEND(sendbuf, count, MPI_REAL, 0, tag, comm, ierr) CALL MPI_RECV(recvbuf, count, MPI_REAL, 0, tag, comm, status, ierr) END IF

Сообщение, посланное каждым процессом, должно быть скопировано перед окончанием операции посылки и до старта операции приема. Чтобы завершить программу, необходимо, чтобы по крайней мере одно из двух сообщений было буферизовано. Поэтому программа может быть буферизованной только в случае, если коммуникационная система может буферизовать по крайней мере count слов данных.

Совет пользователям: Когда используется стандартная операция посылки, могут возникать тупиковые ситуации, когда оба процесса блокированы из-за недоступности буферного пространства. То же самое будет случаться, если используется синхронный режим. Если используется буферизованный режим и недостаточно буферного пространства, программа также не будет завершена. Однако, это будет не дедлок , а ошибка переполнения буфера.

Программа ``защищена'', если никакое буферизуемое сообщение не требует прекращения программы. В такой программе можно все посылки заменить синхронными передачами. Этот консервативный стиль программирования обеспечивает наилучшую переносимость программ, поскольку завершение программы не зависит от количества доступного буферного пространства или используемого коммуникационного протокола.

Многие программисты предпочитают иметь больше свободы действий и иметь возможность использовать ``незащищенный'' программный стиль, показанный в примере 3.9. В таком случае использование стандартных посылок должно обеспечить наилучший компромисс между характеристиками и надежностью: хорошие реализации будут обеспечивать достаточную буферизацию, чтобы программа, как правило, была без дедлоков. Буферизованный режим передачи может быть использован для программ, которые требуют больше буферизации или в ситуации, где программист желает иметь больше возможностей управления. Этот режим также может быть использован для целей отладки, так как условия переполнения буфера диагностировать легче, чем определить наличие дедлока.

Неблокируемые операции обмена, как описано в разделе 3.7, можно использовать, чтобы избежать необходимости буферизации исходящих сообщений. Это предупреждает дедлоки из-за недостатка буферного пространства и улучшает характеристики, поскольку позволяет совмещать вычисления и обмен и исключает накладные расходы на распределение буферов и копирование сообщений в буфера.[]



Alex Otwagin 2002-12-10