Этот раздел содержит уточнения и небольшую коррекцию версии стандарта MPI-1.1. Единственная новая функцияв в MPI-1.2 - это функция, указывающая, какая версия стандарта используется. Между MPI-1 и MPI-1.1 имеются небольшие различия. Между MPI-1.1 и MPI-1.2 имеются очень небольшие различия (только те, которые обсуждены в этом разделе), но разница между MPI-1.1 и MPI-1.2 принципиальная.
С1. Номер версии
Номер версии стандарта можно определить как на этапе компиляции, так и во время исполнения программы. ``Версия'' представляется двумя целыми величинами - для версии и подверсии:
В Си и С++:
# define MPI_VERSION 1
# define MPI_SUBVERSION 2
в языке ФОРТРАН:
INTEGER MPI_VERSION, MPI_SUBVERSION
PARAMETER (MPI_VERSION = 1)
PARAMETER (MPI\_SUBVERSION = 2)
Для вызова во время исполнения используется функция MPI_GET_VERSION, синтаксис которой представлен ниже.
MPI_GET_VERSION(version, subversion)
OUT | version | номер версии (целое) | |
OUT | subversion | номер подверсии (целое) |
int MPI_Get_version(int *version, int *subversion)
MPI_GET_VERSION(VERSION, SUBVERSION, IERROR)
INTEGER VERSION, SUBVERSION, IERROR
MPI_GET_VERSION - одна из немногих функций, которая может быть вызвана перед MPI_INIT и после MPI_FINALIZE.
Привязки для С++ можно найти в приложении В
С2 Уточнения для MPI-1.0 и MPI-1.1
На основе опыта использования версий MPI-1.0 и MPI-1.1 MPI Forum предлагает уточнения для некоторых функций, в дальнейшем эти функции должны использоваться в соответствии с этими уточнениями.
С2.1 Уточнения для MPI_INITIALIZED
MPI_INITIALIZED возвращает true, если вызывающий процесс обращался к MPI_INIT. На поведение MPI_INITIALIZED не влияет тот факт, был ли уже вызван MPI_FINALIZE или нет.
С2.2 Уточнение MPI_FINALIZE
Эта процедура очищает все состояния MPI. Каждый процесс обязан вызвать MPI_FINALIZE перед своим выходом . Если не была вызвана функция MPI_ABORT, каждый процесс должен завершить все неблокирующие ждущие обмены до обращения к MPI_FINALIZE. Более того, на момент обращения к MPI_FINALIZE для всех ждущих посылок должен быть установлен соответствующий прием, и для всех ждущих приемов должна быть установлена соответствующая посылка. Например, следующая программа является корректной:
Process 0 Process 1
--------- ---------
MPI_Init(); MPI_Init();
MPI_Send(dest=1); MPI_Recv(src=0);
MPI_Finalize(); MPI_Finalize();
Без установления соответствующего приема программа неверна:
Process 0 Process 1
----------- -----------
MPI_Init(); MPI_Init();
MPI_Send (dest=1);
MPI_Finalize(); MPI_Finalize();
Успешное возвращение из операции блокирующего обмена или из MPI_WAIT или MPI_TEST говорит пользователю, что буфер может использоваться повторно, и означает, что операция завершена пользователем, но не гарантирует, что вся работа в локальном процессе завершена. Успешное возвращение из MPI_REQUEST_FREE с дескриптором запроса, сгенерированным MPI_ISEND, обнуляет дескриптор, но не дает гарантии, что операция завершена. MPI_ISEND завершается только тогда, когда он узнает некоторым способом, что соответствующий прием завершен.
MPI_FINALIZE гарантирует, что все локальные действия, необходимые для обменов, которые пользователь уже завершил, будут иметь место в действительности перед его возвратом.
MPI_FINALIZE не гарантирует ничего относительно ждущих обменов, которые еще не завершены (завершение гарантируется только MPI_WAIT, MPI_TEST, или MPI_REQUEST_FREE совместно с некоторыми другими проверками завершения).
Эта программа верна:
rank 0 rank 1
=====================================================
... ...
MPI_Isend(); MPI_Recv();
MPI_Request_free(); MPI_Barrier();
MPI_Barrier(); MPI_Finalize();
MPI_Finalize(); exit();
exit();
Эта программа неверна и ее поведение является неопределенным:
rank 0 rank 1
=====================================================
... ...
MPI_Isend(); MPI_Recv();
MPI_Request_free(); MPI_Finalize();
MPI_Finalize(); exit();
exit();
Если между MPI_BSEND (или другой буферизованной посылки) и MPI_FINALIZE нет операции MPI_BUFFER_DETACH, MPI_FINALIZE неявно обеспечивает MPI_BUFFER_DETACH.
Нижеследующая программа правильная и после MPI_Finalize все выполняется так, как если бы буфер был подключен:
rank 0 rank 1
=====================================================
... ...
buffer = malloc(1000000); MPI_Recv();
MPI_Buffer_attach(); MPI_Finalize();
MPI_Bsend(); exit();
MPI_Finalize();
free(buffer);
exit();
В следующем примере MPI_Iprobe() должен возвратить флаг FALSE. MPI_Test_cancelled() обязан возвратить флаг TRUE, независимо от относительного порядка выполнения MPI_Cancel() в процессе 0 и MPI_Finalize() в процессе 1. Вызов MPI_Iprobe() необходим для создания уверенности, что реализация знает о том, что сообщение ``tag1'' существует на стороне приема.
Номер 0 номер 1
========================================================
MPI_Init(); MPI_Init();
MPI_Isend(tag1);
MPI_Barrier(); MPI_Barrier();
MPI_Iprobe(tag2);
MPI_Barrier(); MPI_Barrier();
MPI_Finalize();
exit();
MPI_Cancel();
MPI_Wait();
MPI_Test_cancelled();
MPI_Finalize();
exit();
После того, как MPI_FINALIZE возвратил управление, не могут быть вызваны никакие процедуры MPI (даже MPI_INIT), исключая MPI_GET_VERSION, MPI_INITIALIZED, и MPI-2 функция MPI_FINALIZED. Каждый процесс обязан завершить все ждущие коммуникации, которые он инициировал, до того, как он вызовет MPI_FINALIZE. Если вызов возвращает управление, каждый процесс может продолжать локальные вычисления или осуществить выход, без участия в дальнейших MPI обменах с другими процесами. MPI_FINALIZE является коллективной операцией на коммуникаторе MPI_COMM_WORLD.
Совет разработчикам: Хотя процесс завершил все инициированные коммуникации, такие коммуникации все еще могут быть не завершены с точки зрения более низкого системного уровня MPI. Например, блокирующая посылка может быть уже завершена, хотя данные все еще буферизованы на процессе-отправителе. Реализация MPI обязана гарантировать, что процесс завершает любую инициированную коммуникацию перед возвращением MPI_FINALIZE. Поэтому, если процесс существует после вызова MPI_FINALIZE, это не будет вызывать отказа продолжающегося обмена.
Не требуется, чтобы все процессы возвращали управление из
MPI_FINALIZE, однако необходимо, чтобы по крайней мере процесс
0 в MPI_COMM_WORLD возвратил управление, чтобы
пользователь знал о том, что порция вычислений завершена.
Следующий пример иллюстрирует требование, чтобы по крайней мере один
процесс возвращал управление и чтобы было известно, что процесс 0 -
один из таких процессов.
...
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
...
MPI_Finalize();
if (myrank == 0) {
resultfile = fopen("outfile","w");
dump_results(resultfile);
fclose(resultfile);
}
exit(0);
С2.3 Уточнение статуса после MPI_WAIT и MPI_TEST
Поля в статусном объекте, возвращенные вызовами MPI_WAIT, MPI_TEST или любыми другими производными функциями (MPI_{TEST,WAIT}{ALL,SOME,ANY}), где запрос соответствует вызову посылки, являются неопределенными за двумя исключениями: поле ошибки статусного объекта будет содержать правильную информацию, если вызов wait или test возвратил MPI_ERR_IN_STATUS; и возвращенный статус может быть запрошен вызовом MPI_TEST_CANCELLED.
Коды ошибок, принадлежащие классу MPI_ERR_IN_STATUS, должны быть возвращены только завершающей функцией MPI, которая использует массивы MPI_STATUS. Для функций ( MPI_TEST, MPI_TESTANY, MPI_WAIT, MPI_WAITANY), которые возвращают единственное значение MPI_STATUS, должен быть использован нормальный процесс возврата ошибки ( а не поле MPI_ERROR в аргументе MPI_STATUS).
С2.4 Уточнение MPI_INTERCOMM_CREATE
Проблема : стандарт MPI-1 говорит в дискуссии о MPI_INTERCOMM_CREATE, что: ``Группы должны быть разъединены'' и что ``Лидером может быть тот же самый процесс''.
Причиной требования ``Группы должны быть разъединены'' является озабоченность реализацией MPI_INTERCOMM_CREATE, которая не применима в случае, когда лидером является тот же самый процесс.
Принято: удалить текст: ``(два лидера могут быть тем же самым процессом)'' из дискуссии о MPI_INTERCOMM_CREATE.
Заменить текст ``Все конструкторы интер-коммуникаторов являются блокирующими и требуют, чтобы локальные и удаленные группы были разъединены в таком порядке, чтобы избежать дедлока'' текстом ``Все конструкторы интер-коммуникаторов являются блокирующими и требуют, чтобы локальные и удаленные группы были разъединены''.
Совет пользователям: Группы должны быть разъединены по нескольким причинам. Прежде всего, это цель интер-коммуникаторов - обеспечить коммуникатор для обмена между разъединенными группами. Это отражено в определении MPI_INTERCOMM_MERGE, который позволяет пользователю управлять нумерацией процессов в созданном интер-коммуникаторе; эта нумерация имеет мало смысла, если группы не разъединены. К тому же, естественное расширение на коллективные операции для интер-коммуникаторов имеет наибольший смысл, когда группы разъединены.
С2.5 Уточнение MPI_INTERCOMM_MERGE
Обработчик ошибок в новом интер-коммуникаторе в каждом процессе наследуется из коммуникатора, который создала локальная группа. Заметим, что это может выразиться в том, что различные процессы одного и того же коммуникатора имеют различные обработчики ошибок.
С.2.6. Уточнение привязок MPI_TYPE_SIZE
Это уточнение необходимо для описания MPI_TYPE_SIZE в MPI-1, поскольку проблема возникает многократно. Это уточнение привязки.
Совет пользователям: Стандарт MPI-1 указывает, что выходной аргумент MPI_TYPE_SIZE в Си имеет тип int. MPI Forum рассмотрел предложение изменить это и решил оставить исходное решение.
С2.7. Уточнение MPI_REDUCE
Текст на стр. 115, строки 25 - 28 стандарта MPI-1.1 (12, июнь, 1995) говорит:
``Аргумент типа данных MPI_REDUCE обязан быть совместим с типом ор. Предопределенные операторы работают только с типами данных из списка, представленного в разделах 4.9.2 и 4.9.3. Операторы, определенные пользователем, могут работать с производными типами данных''.
Этот текст заменен на:
``Аргумент типа данных MPI_REDUCE обязан быть совместим с типом ор. Предопределенные опраторы работают только с типами данных из списка, представленного в разделах 4.9.2 и 4.9.3. Более того, типы данных и ор, данные для предопределенных операторов, обязаны быть одними и теми же на всех процессах''.
Заметим, что пользователь имеет возможность создавать различные определенные пользователем операции в каждом процессе. MPI не определяет, какие операции и с какими операндами используются в этом случае.
Совет пользователям: Пользователи не должны делать никаких
предположений о том, как реализован MPI_REDUCE. Защита должна
гарантировать, что та же самая функция передается в MPI_REDUCE
каждым процессом.
Перекрывающиеся типы данных разрешены в буферах посылки. Перекрывающиеся типы данных в буферах приема ошибочны и могут давать непредсказуемый результат.
С2.8. Уточнение поведения функции Attribute Callback при ошибках
Если функция копирования или удаления атрибута возвращает что-либо, отличное от MPI_SUCCESS, то обращение, которое вызвало эту ситуацию (например, MPI_COMM_FREE), является неверным.
С2.9. Уточнение MPI_PROBE и MPI_IPROBE
Страница 52, строки 1 -3 MPI-1.1 (12, июнь, 1995) устанавливает, что:
``Последующий прием, выполненный с тем же самым коммуникатором, номером процесса-отправителя и тэгом, возвращенным в статус функцией MPI_IPROBE, будет принимать сообщение, которое соответствует пробе, если не имело места никакое другое пересекающееся сообщение после пробы, и посылка не была успешно отменена перед приемом''.
Объяснение: Следующая программа показывает, что определения MPI-1 для отмены и пробы конфликтуют:
Процесс 0 Процесс 1
---------- ----------
MPI_Init(); MPI_Init();
MPI_Isend(dest=1);
MPI_Probe();
MPI_Barrier(); MPI_Barrier();
MPI_Cancel();
MPI_Wait();
MPI_Test_cancelled();
MPI_Barrier(); MPI_Barrier();
MPI_Recv();
Поскольку посылка была отменена процессом 0, ожидание обязано быть локальным (страница 54, строка 13) и обязано возвращать управление перед соответствующим приемом. Чтобы ожидание было локальным, посылка должна быть успешно отменена и поэтому не обязана соответствовать приему в процессе 1 (страница 54, строка 29).
Однако, ясно, что проба на процессе 1 обязана в конечном счете детектировать входное сообщение. На странице 52 в строке 1 ясно говорится, что последующий прием процессом 1 обязан возвратить опробованное сообщение.
Выше выявлено противоречие и поэтому текст ``...и отправка не завершена успешно перед приемом'' обязан быть добавленным к строке 3 на странице 54.
Альтернативное решение (отброшенное) потребовало бы изменения семантики отмены, так как вызов не является локальным, если сообщение уже опробовано. Это увеличивает сложность реализации и добавляет новую концепцию ``состояния'' к сообщению (опробовано или нет). Однако, это сохранило бы ту особенность, что блокирующий прием после пробы является локальным.