Руководство по MPI (Руководство по MPI)

Посмотреть архив целиком

2



АННОТАЦИЯ


Документ содержит сведения, необходимые для программистов, работающих на СК “МВС-1000М” и пользующихся услугами компонентов программного обеспечения. Он состоит из пяти разделов.

В разделе 1 представлены сведения о назначении, функциях и условиях применения среды параллельного программирования, а также всего ОПО СК “МВС-1000М” в целом.

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

В разделе 3 содержится описание процедур MPI.

В разделе 4 приведены сведения о подготовке прикладных программ к выполнению на вычислительных блоках СК “МВС-1000М”.

В разделе 5 представлена информация о средствах отладки и профилирования программных модулей.


СОДЕРЖАНИЕ


1. Назначение и условия применения среды параллельного программирования СК “МВС-1000М” 4

1.1. Назначение и состав среды параллельного программирования 4

1.2. Условия эксплуатации 5

1.3. Терминология и обозначения 5

2. Характеристики среды параллельного программирования 6

2.1. Особенности программного интерфейса MPI 6

2.2. Общие принципы функционирования 6

2.3. Директории пользователя 7

3. Обращение к MPI 7

3.1. Основные сведения 7

3.2. Общие процедуры MPI 7

3.3. Процедуры приема/передачи сообщений 8

3.4. Процедуры коллективного взаимодействия процессов 17

3.5. Процедура синхронизации процессов 19

3.6. Процедуры для работы с группами процессов 19

3.7. Предопределенные константы и типы данных 20

3.8. Латентность и пропускная способность 22

4. Входные и выходные данные 23

4.1. Этапы создания программ. 23

4.2. Компиляция модулей. 24

4.3. Запуск программ, использующих MPI. 24

5. Средства отладки и профилирования 25

5.1. Обработчики ошибок 25

5.2. Профилировочные библиотеки…………………………………………….25

5.3. Аргументы командной строки для mpirun 28

5.4. Аргументы MPI для программ пользователя 28

Приложение А. Примеры программ с использованием функций MPI 30

Перечень ссылочных документов 59















1 Назначение и условия применения среды параллельного программирования СК “МВС-1000М”

1.1 Назначение и состав среды параллельного программирования

Компоненты общего программного обеспечения (ОПО) СК “МВС-1000М” поддерживают все этапы разработки параллельных программ пользователей, а также обеспечивают непосредственно выполнение процессов содержательной обработки на решающем поле вычислительных модулей. Они функционируют на вычислительных модулях (ВМ) и управляющей рабочей станции (УРС).

В качестве среды для параллельного программирования используется программный интерфейс MPI, который фактически является стандартом для разработчиков параллельных программ.

Среда параллельного программирования реализована на базе интерфейса передачи сообщений MPI (Message Passing Interface) и включает в себя пакет MPICH for GM.

Информацию об особенностях работы с программным пакетом MPICH можно найти в руководстве пользователя по MPICH [2].

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

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

Стандарты MPI 1.2 и MPI 2.0 доступны на сайте http://www.mpi-forum.org в формате HTML. Более подробную информацию о работе с MPI можно найти в книгах, посвященных MPI [4, 5].

Синтаксис MPI облегчает создание приложений в модели SPMD (single program multiple data) - одна программа работает в разных процессорах со своими данными. Одна и та же функция сможет вызываться на узле-источнике и узлах-приемниках, а тип выполняемой операции (передача или прием) в этом случае определяется с помощью параметра. Такой синтаксис вызовов делает SPMD-программы существенно компактнее, хотя и труднее для понимания.

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

1.2 Условия эксплуатации

Для работы компонентов программного обеспечения СК “МВС-1000М” необходимы:

  • управляющая рабочая станция Alpha 21264 на основе материнской платы UP2000;

  • двухпроцессорные вычислительные модули на базе процессора Alpha21264;

  • коммуникационная среда Myrinet, состоящая из сетевых плат, устанавливаемых в ВМ, и коммутаторов;

  • коммуникационная среда FastEthernet, состоящая из сетевых плат, устанавливаемых в ВМ и УРС, и коммутаторов.

В состав ОПО СК “МВС-1000М” входят:

  • операционные системы ВМ и управляющей рабочей станции;

  • инструментальные программные средства;

  • программные средства высокопроизводительной коммуникационной среды Myrinet;

  • программные средства коммуникационной среды FastEthernet;

  • операционная среда параллельного программирования;

  • подсистема удаленного доступа.

На управляющей рабочей станции СК “МВС-1000М” и ВМ установлена операционная система Linux версии 6.2.

В состав инструментальных программных средств входят:

  • компиляторы GNU, поставляемые в составе ОС Linux: C, C++ и Fortran77 (команды gcc, c++ и f77 соответственно);

  • компиляторы фирмы Compaq для ОС Linux на платформе Alpha: С, С++, Fortran (команды ccc ,схх и fort соответственно);

  • редактор связей;

  • отладчики (gdb, dbx, xxgdb).

Коммуникационная среда Myrinet поддерживается в современных реализациях интерфейса параллельного программирования MPI. В качестве программных средств коммуникационной среды Myrinet используется коммуникационная система GM. В ее состав входят:

  • драйвер;

  • служебные программы;

  • тестовые программы;

  • библиотека функций и заголовочный файл GM API;

  • демонстрационные программы.

    1. Терминология и обозначения

При работе с интерфейсом MPI используются следующие термины и обозначения:

а) процесс – это исполнение программы на одном процессоре, на котором установлен MPI, безотносительно к тому, содержит ли эта программа внутри параллельные ветви или операции ввода/вывода или просто последовательный программный код;

б) группа – представляет собой совокупность процессов, каждый из которых имеет внутри группы уникальное имя, используемое для взаимодействия с другими процессами группы посредством коммуникатора группы;

в) коммуникатор группы – реализует обмены данными между процессами и их синхронизацию. Собственно, коммуникатор выступает для прикладной группы как коммуникационная среда для взаимодействия. Коммуникаторы бывают внутригрупповыми (intra) и межгрупповыми(inter). Коммуникатор определяет контекст передачи сообщений. Сообщения, использующие разные коммуникаторы, не оказывают влияния друг на друга и не взаимодействуют. Каждая группа процессов использует отдельный коммуникатор. Процессы внутри группы имеют номера от 0 до n-1, где n – количество процессов в группе [9].


2 Характеристики среды параллельного программирования

2.1 Особенности программного интерфейса MPI

MPI расшифровывается как Message Passing Interface - Интерфейс передачи сообщений, т.е. конкретному стандарту присвоено название всего представляемого им класса программного инструментария. В пакет, реализующий программный интерфейс на СК “МВС-1000М”, входят два обязательных компонента:

  • библиотека программирования для языков Си, Си++ и Фортран;

  • загрузчик исполняемых файлов.

Характеристиками программного интерфейса MPI являются:

  • обеспечение связи между ветвями параллельной программы;

  • программирование по методу SPMD (Single Program – Multiple Data) с передачей сообщений;

  • реализация в виде библиотеки для языков программирования C и Фортран и загрузчика приложений;

  • поддержка гетерогенных вычислений;

  • мобильность интерфейса и, как следствие, мобильность создаваемых программ.

2.2 Общие принципы функционирования

Среда параллельного программирования, используемая на СК “МВС-1000М”, имеет следующие особенности:

  • параллельная программа содержит код для всех ветвей сразу;

  • загрузчиком запускается указываемое количество экземпляров программы;

  • каждый экземпляр программы определяет свой порядковый номер, и, в зависимости от этого номера и общего размера вычислительного поля, выполняет ту или иную ветвь алгоритма;

  • каждая ветвь имеет собственное пространство данных, полностью изолированное от других ветвей;

  • ветви обмениваются данными только с помощью передачи сообщений операционной среды параллельного программирования;

  • запуск MPI-приложения осуществляется с УРС;

  • запускаемый файл MPI-приложения должен быть доступен на каждом ВМ по тому же абсолютному пути, что и на управляющей рабочей станции.

2.3 Директории пользователя

Домашние директории пользователей находятся в директории /home. Она монтируется с файлового сервера СК «МВС-1000М».

Директория /store является локальной для каждого вычислительного модуля. В нее замонтирован раздел размером не менее 15 GB. Прикладные программы могут использовать эту директорию для хранения локальных данных. После завершения работы с этими данными их необходимо удалить из директории /store.


3 Обращение к MPI

3.1 Основные сведения

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

Процедуры MPI подразделяются на следующие группы:

  • общие процедуры;

  • процедуры приема / передачи сообщений;

  • процедуры коллективного взаимодействия процессов;

  • процедура синхронизации процессов;

  • процедуры для работы с группами процессов.

При описании процедур MPI будем пользоваться словом OUT для обозначения "выходных" параметров, т.е. таких параметров, через которые процедура возвращает результаты.

В случае успешного выполнения всех нижеописанных процедур возвращается код успешного выполнения – MPI_SUCCESS, в противном случае возвращается код ошибки.

3.2 Общие процедуры MPI

Общие процедуры MPI обеспечивают инициализацию и завершение работы процессов, а также сервисные функции.


Процедура MPI_Init

Формат процедуры:

int MPI_Init (int* argc, char** argv)

Данная процедура обеспечивает инициализацию параллельной части приложения. Реальная инициализация для каждого приложения выполняется не более одного раза, а если MPI уже был инициализирован, то никакие действия не выполняются, а происходит немедленный возврат из подпрограммы. Все оставшиеся MPI-процедуры могут быть вызваны только после вызова MPI_Init.


Процедура MPI_Finalize

Формат процедуры:

int MPI_Finalize(void)

Данная процедура обеспечивает завершение параллельной части приложения. Все последующие обращения к любым MPI-процедурам, в том числе к MPI_Init, запрещены. К моменту вызова MPI_Finalize некоторым процессом все действия, требующие его участия в обмене сообщениями, должны быть завершены.

Сложный тип аргументов MPI_Init предусмотрен для того, чтобы передавать всем процессам аргументы main:

int main(int argc, char** argv)

{

MPI_Init(&argc, &argv);

...

MPI_Finalize();

}


Процедура MPI_Comm_size

Формат процедуры:

int MPI_Comm_size(MPI_Comm comm, int* size), где

  • comm - идентификатор группы;

  • OUT size - размер группы.

Данная процедура обеспечивает определение общего числа параллельных процессов в группе comm.


Процедура MPI_Comm_rank

Формат процедуры:

int MPI_Comm_rank(MPI_comm comm, int* rank), где

  • comm - идентификатор группы;

  • OUT rank - номер вызывающего процесса в группе comm.

Данная процедура обеспечивает определение номера процесса в группе comm. Значение, возвращаемое по адресу &rank, лежит в диапазоне от 0 до size_of_group-1.


Процедура MPI_Wtime

Формат процедуры:

double MPI_Wtime(void)

Данная процедура возвращает астрономическое время в секундах (вещественное число), прошедшее с некоторого момента в прошлом. Гарантируется, что этот момент не будет изменен за время существования процесса.

    1. Процедуры приема / передачи сообщений

Процедуры данной группы составляют основу взаимодействия процессов. Базовым механизмом связи между процессами в MPI является посылка и прием сообщений. Основными функциями, обеспечивающими связь “точка-точка” являются операторы send и receive.


Процедура MPI_Send

Формат процедуры:

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 должен указываться с помощью предопределенных констант типа.

Разрешается передавать сообщение самому себе.

Блокировка гарантирует корректность повторного использования всех параметров после возврата из подпрограммы. Выбор способа осуществления этой гарантии: копирование в промежуточный буфер или непосредственная передача процессу dest, остается за MPI. Следует специально отметить, что возврат из подпрограммы MPI_Send не означает ни того, что сообщение уже передано процессу dest, ни того, что сообщение покинуло процессорный элемент, на котором выполняется процесс, выполнивший MPI_Send.


Процедура MPI_Recv

Формат процедуры:

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_Probe.

Блокировка гарантирует, что после возврата из подпрограммы все элементы сообщения приняты и расположены в буфере buf.

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

Если процесс посылает два сообщения другому процессу, и оба эти сообщения соответствуют одному и тому же вызову MPI_Recv, то первым будет принято то сообщение, которое было отправлено раньше.


Процедура MPI_Get_Count

Формат процедуры:

int MPI_Get_Count(MPI_Status *status, MPI_Datatype datatype, int *count), где

  • status - параметры принятого сообщения;

  • datatype - тип элементов принятого сообщения;

  • OUT count - число элементов сообщения.

По значению параметра status данная процедура определяет число уже принятых (после обращения к MPI_Recv) или принимаемых (после обращения к MPI_Probe или MPI_IProbe) элементов сообщения типа datatype.


Процедура MPI_Probe

Формат процедуры:

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. Следует обратить внимание, что подпрограмма определяет только факт прихода сообщения, но реально его не принимает.


Процедура MPI_Isend

Формат процедуры:

int MPI_ISend(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_Send, однако возврат из процедуры происходит сразу после инициализации процесса передачи без ожидания обработки всего сообщения, находящегося в буфере buf. Это означает, что нельзя повторно использовать данный буфер для других целей без получения дополнительной информации о завершении данной посылки. Окончание процесса передачи (т.е. того момента, когда можно переиспользовать буфер buf без опасения испортить передаваемое сообщение) можно определить с помощью параметра request и процедур MPI_Wait и MPI_Test.

Сообщение, отправленное любой из процедур MPI_Send и MPI_ISend, может быть принято любой из процедур MPI_Recv и MPI_IRecv.


Процедура MPI_Irecv

Формат процедуры:

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 и MPI_Test.


Процедура MPI_Wait

Формат процедуры:

int MPI_Wait(MPI_Request *request, MPI_Status *status), где

  • request - идентификатор асинхронного приема или передачи;

  • OUT status - параметры сообщения.

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


Процедура MPI_WaitAll

Формат процедуры:

int MPI_WaitAll(int count, MPI_Request *requests, MPI_Status *statuses), где

  • count - число идентификаторов;

  • requests - массив идентификаторов асинхронного приема или передачи;

  • OUT statuses - параметры сообщений.

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


Процедура MPI_WaitAny

Формат процедуры:

int MPI_WaitAny(int count, MPI_Request *requests, int *index, MPI_Status *status), где

  • count - число идентификаторов;

  • requests - массив идентификаторов асинхронного приема или передачи;

  • OUT index - номер завершенной операции обмена;

  • OUT status - параметры сообщений.

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


Процедура MPI_WaitSome

Формат процедуры:

int MPI_WaitSome(int incount, MPI_Request *requests, int *outcount, int *indexes, MPI_Status *statuses), где

  • incount - число идентификаторов;

  • requests - массив идентификаторов асинхронного приема или передачи;

  • OUT outcount - число идентификаторов завершившихся операций обмена;

  • OUT indexes - массив номеров завершившихся операций обмена;

  • OUT statuses - параметры завершившихся сообщений.

Выполнение процесса блокируется до тех пор, пока, по крайней мере, одна из операций обмена, ассоциированных с указанными идентифика-торами, не будет завершена. Параметр outcount содержит число завершенных операций, а первые outcount элементов массива indexes содержат номера элементов массива requests с их идентификаторами. Первые outcount элементов массива statuses содержат параметры завершенных операций.


Процедура MPI_Test

Формат процедуры:

int MPI_Test( MPI_Request *request, int *flag, MPI_Status *status), где

  • request - идентификатор асинхронного приема или передачи;

  • OUT flag - признак завершенности операции обмена;

  • OUT status - параметры сообщения.

Данная процедура осуществляет проверку завершенности асинхронных процедур MPI_ISend или MPI_IRecv, ассоциированных с идентифика-тором request. В параметр flag возвращается значение 1, если соответствующая операция завершена, и значение 0 в противном случае. Если завершена процедура приема, то атрибуты и длину полученного сообщения можно определить обычным образом с помощью параметра status.


Процедура MPI_TestAll

Формат процедуры:

int MPI_TestAll( int count, MPI_Request *requests, int *flag, MPI_STatus *statuses), где

  • count - число идентификаторов;

  • requests - массив идентификаторов асинхронного приема или передачи;

  • OUT flag - признак завершенности операций обмена;

  • OUT statuses - параметры сообщений.

В результате выполнения данной процедуры, параметр flag возвращается значение 1, если все операции, ассоциированные с указанными идентификаторами, завершены (с указанием параметров сообщений в массиве statuses). В противном случае возвращается 0, а элементы массива statuses не определены.


Процедура MPI_TestAny

Формат процедуры:

int MPI_TestAny(int count, MPI_Request *requests, int *index, int *flag, MPI_Status *status), где

  • count - число идентификаторов;

  • requests - массив идентификаторов асинхронного приема или передачи;

  • OUT index - номер завершенной операции обмена;

  • OUT flag - признак завершенности операции обмена;

  • OUT status - параметры сообщения.

Если к моменту вызова данной процедуры хотя бы одна из операций обмена завершилась, то в параметр flag возвращается значение 1, index содержит номер соответствующего элемента в массиве requests, а status - параметры сообщения.


Процедура MPI_TestSome

Формат процедуры:

int MPI_TestSome(int incount, MPI_Request *requests, int *outcount, int *indexes, MPI_Status *statuses), где

  • incount - число идентификаторов;

  • requests - массив идентификаторов асинхронного приема или передачи;

  • OUT outcount - число идентификаторов завершившихся операций обмена;

  • OUT indexes - массив номеров завершившихся операций обмена;

  • OUT statuses - параметры завершившихся операций.

Данная процедура работает так же, как и MPI_TestAny, за исключением того, что возврат происходит немедленно. Если ни одна из указанных операций не завершилась, то значение outcount будет равно нулю.


Процедура MPI_Iprobe

Формат процедуры:

int MPI_Iprobe( int source, int msgtag, MPI_Comm comm, int *flag, MPI_Status *status), где

  • source - номер процесса-отправителя или MPI_ANY_SOURCE;

  • msgtag - идентификатор ожидаемого сообщения или MPI_ANY_TAG;

  • comm - идентификатор группы;

  • OUT flag - признак завершенности операции обмена;

  • OUT status - параметры обнаруженного сообщения.

Данная процедура обеспечивает получение информации о поступлении и структуре ожидаемого сообщения без блокировки. В параметр flag возвращается знаачение 1, если сообщение с подходящими атрибутами уже может быть принято (в этом случае ее действие полностью аналогично MPI_Probe), и значение 0, если сообщения с указанными атрибутами еще нет.

Для снижения накладных расходов, возникающих в рамках одного процессора при обработке приема/передачи и перемещении необходимой информации между процессом и сетевым контроллером, могут использоваться процедуры: MPI_Send_Init, MPI_Recv_Init и MPI_Start_All. Несколько запросов на прием и/или передачу могут объеди-няться вместе для того, чтобы далее их можно было бы запустить одной командой. Способ приема сообщения никак не зависит от способа его посылки: сообщение, отправленное с помощью объединения запросов либо обычным способом, может быть принято как обычным способом, так и с помощью объединения запросов.


Процедура MPI_Send_Init

Формат процедуры:

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_Recv_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_IReceive, однако в отличие от нее реальный прием не начинается до вызова подпрограммы MPI_StartAll.


Процедура MPI_Start_All

Формат процедуры:

int MPI_Start_All (int count, MPI_Request *requests), где

  • count - число запросов на взаимодействие;

  • OUT requests - массив идентификаторов приема/передачи.

Данная процедура обеспечивает запуск всех отложенных взаимодействий, ассоциированных вызовами подпрограмм MPI_Send_Init и MPI_Recv_Init с элементами массива запросов requests. Все взаимодействия запускаются в режиме без блокировки, а их завершение можно определить обычным образом с помощью процедур MPI_Wait и MPI_Test.


Процедура MPI_Sendrecv

Формат процедуры:

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_Sendrecv, может быть принято обычным образом, и точно также операция MPI_Sendrecv может принять сообщение, отправленное обычной операцией MPI_Send. Буфера приема и посылки обязательно должны быть различными.

    1. Процедуры коллективного взаимодействия процессов

Для коллективного взаимодействия процессов используются процедуры MPI_Bcast, MPI_Gather, MPI_AllReduce и MPI_Reduce. В процессе коллективного взаимодействия участвуют все процессы приложения. Соответствующая процедура должна быть вызвана каждым процессом, быть может, со своим набором параметров. Возврат из процедуры коллективного взаимодействия может произойти в тот момент, когда участие процесса в данной операции уже закончено. Как и для блокирующих процедур, возврат означает то, что разрешен свободный доступ к буферу приема или посылки, но не означает ни того, что операция завершена другими процессами, ни даже того, что она ими начата (если это возможно по смыслу операции).

Процедура MPI_Bcast

Формат процедуры:

int MPI_Bcast (void *buf, int count, MPI_Datatype datatype, int source, MPI_Comm comm), где

  • OUT buf - адрес начала буфера посылки сообщения;

  • count - число передаваемых элементов в сообщении;

  • datatype - тип передаваемых элементов;

  • source - номер рассылающего процесса;

  • comm - идентификатор группы.

Процедура обеспечивает рассылку сообщения от процесса source всем процессам, включая рассылающий процесс. При возврате из процедуры содержимое буфера buf процесса source будет скопировано в локальный буфер процесса. Значения параметров count, datatype и source должны быть одинаковыми у всех процессов.


Процедура MPI_Gather.

Формат процедуры:

int MPI_Gather(void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype, int dest, MPI_Comm comm), где

  • sbuf - адрес начала буфера посылки;

  • scount - число элементов в посылаемом сообщении;

  • stype - тип элементов отсылаемого сообщения;

  • OUT rbuf - адрес начала буфера сборки данных;

  • rcount - число элементов в принимаемом сообщении;

  • rtype - тип элементов принимаемого сообщения;

  • dest - номер процесса, на котором происходит сборка данных;

  • comm - идентификатор группы;

  • OUT ierror - код ошибки.

Данная процедура обеспечивает сбор данных со всех процессов в буфере rbuf процесса dest. Каждый процесс, включая dest, посылает содержимое своего буфера sbuf процессу dest. Собирающий процесс сохраняет данные в буфере rbuf, располагая их в порядке возрастания номеров процессов. Параметр rbuf имеет значение только на собирающем процессе и на остальных игнорируется, значения параметров count, datatype и dest должны быть одинаковыми у всех процессов.


Процедура MPI_AllReduce

Формат процедуры:

int MPI_AllReduce(void *sbuf, void *rbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm), где

  • sbuf - адрес начала буфера для аргументов;

  • OUT rbuf - адрес начала буфера для результата;

  • count - число аргументов у каждого процесса;

  • datatype - тип аргументов;

  • op - идентификатор глобальной операции;

  • comm - идентификатор группы.

Процедура обеспечивает выполнение count глобальных операций op с возвратом count результатов во всех процессах в буфере rbuf. Операция выполняется независимо над соответствующими аргументами всех процессов. Значения параметров count и datatype у всех процессов должны быть одинаковыми. Из соображений эффективности реализации предполагается, что операция op обладает свойствами ассоциативности и коммутативности.


Процедура MPI_Reduce.

Формат процедуры:

int MPI_Reduce(void *sbuf, void *rbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm), где

  • sbuf - адрес начала буфера для аргументов;

  • OUT rbuf - адрес начала буфера для результата;

  • count - число аргументов у каждого процесса;

  • datatype - тип аргументов;

  • op - идентификатор глобальной операции;

  • root - процесс-получатель результата;

  • comm - идентификатор группы.

Процедура аналогична предыдущей, но результат будет записан в буфер rbuf только у процесса root.

3.5 Процедура синхронизации процессов

Процедура синхронизации процессов блокирует работу процессов, вызвавших данную процедуру, до тех пор, пока все оставшиеся процессы группы comm также не выполнят эту процедуру.


Процедура MPI_Barrier

Формат процедуры:

int MPI_Barrier(MPI_Comm comm), где comm - идентификатор группы.


3.6 Процедуры для работы с группами процессов

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

Процедуры, предназначенные для работы с группами процессов, обеспечивают создание и удаление групп (коммуникаторов), а также разбиение групп на подгруппы.

Процедура MPI_Comm_Split

Формат процедуры:

int MPI_Comm_Split(MPI_Comm comm, int color, int key, MPI_Comm *newcomm), где

  • comm - идентификатор группы;

  • color - признак разделения на группы;

  • key - параметр, определяющий нумерацию в новых группах;

  • OUT newcomm - идентификатор новой группы.

Данная процедура разбивает все множество процессов, входящих в группу comm, на непересекающиеся подгруппы - одну подгруппу на каждое значение параметра color (неотрицательное число). Каждая новая подгруппа содержит все процессы одного цвета. Если в качестве color указано значение MPI_UNDEFINED, то в newcomm будет возвращено значение MPI_COMM_NULL.


Процедура MPI_Comm_Free

Формат процедуры:

int MPI_Comm_Free(MPI_Comm comm),

где OUT comm - идентификатор группы.

Процедура уничтожает группу, ассоциированную с идентификатором comm, который после возвращения устанавливается в MPI_COMM_NULL.

3.7 Предопределенные константы и типы данных

В стандарте MPI существует несколько предопределенных типов, среди них:

  • MPI_Status - структура; атрибуты сообщений; содержит три обязательных поля:

  • MPI_Source (номер процесса отправителя);

  • MPI_Tag (идентификатор сообщения – целое неотрицательное число, лежащее в диапазоне от 0 до 32767.);

  • MPI_Error (код ошибки);

  • MPI_Request - системный тип; идентификатор операции посылки-приема сообщения;

  • MPI_Comm - системный тип; идентификатор группы (коммуникатора);

  • MPI_COMM_WORLD - зарезервированный идентификатор группы, состоящей их всех процессов приложения.

Предопределенные константы типа элементов сообщений используются для указания типа пересылаемых данных в процедурах группы приема/передачи. Данные константы представлены в Таблице 1.


Таблица 1

Константы MPI

Тип в C



MPI_CHAR

signed char

MPI_SHORT

signed int

MPI_INT

signed int

MPI_LONG

signed long int

MPI_UNSIGNED_CHAR

unsigned char

MPI_UNSIGNED_SHORT

unsigned int

MPI_UNSIGNED

unsigned int

MPI_UNSIGNED_LONG

unsigned long int

MPI_FLOAT

float

MPI_DOUBLE

double

MPI_LONG_DOUBLE

long double


Более подробно о правилах написания параллельных программ, а также синтаксис процедур MPI можно найти в книге “Writing Message-Passing Parallel Programs with MPI” [6].

С одной стороны, мы передаем в MPI_Recv номер задачи, от которой ждем сообщение, и его идентификатор; а с другой - получаем их от MPI в структуре status? Это сделано потому, что MPI_Recv может быть вызвана с аргументами-джокерами ("принимай что угодно/от кого угодно"), и после такого приема данных программа узнает фактические номер/идентификатор, читая поля MPI_SOURCE и MPI_TAG из структуры status.

Поле MPI_ERROR, как правило, проверять необязательно - обработчик ошибок, устанавливаемый MPI по умолчанию, в случае сбоя завершит выполнение программы до возврата из MPI_Recv. Таким образом, после возврата из MPI_Recv поле status.MPI_ERROR может быть равно только 0 (или, если угодно, MPI_SUCCESS);

Тип MPI_Status не содержит поля, в которое записывалась бы фактическая длина пришедшего сообщения. Длину можно узнать так:

MPI_Status status;

int count;

MPI_Recv( ... , MPI_INT, ... , &status );

MPI_Get_count( &status, MPI_INT, &count );

/* ... теперь count содержит количество принятых ячеек */

Обратите внимание, что аргумент-описатель типа у MPI_Recv и MPI_Get_count должен быть одинаковым, иначе, в зависимости от реализации в count вернется неверное значение; или произойдет ошибка времени выполнения.

Константы-пустышки включают:

  • MPI_COMM_NULL;

  • MPI_DATATYPE_NULL;

  • MPI_REQUEST_NULL.

Константа неопределенного значения используется в процедуре MPI_Comm_Split и имеет имя MPI_UNDEFINED.

Константы глобальных операций используются в процедурах коллективного взаимодействия процессов для указания типа выполняемой операции. Данные константы включают:

  • MPI_MAX;

  • MPI_MIN;

  • MPI_SUM;

  • MPI_PROD.

Константы, определяющие любой процесс/идентификатор, используются для обозначения

  • MPI_ANY_SOURCE;

  • MPI_ANY_TAG.

В стандарте MPI существует несколько предопределенных типов, среди них:

  • MPI_Status - структура; атрибуты сообщений; содержит три обязательных поля:

  • MPI_Source (номер процесса отправителя);

  • MPI_Tag (идентификатор сообщения);

  • MPI_Error (код ошибки);

  • MPI_Request - системный тип; идентификатор операции посылки-приема сообщения;

  • MPI_Comm - системный тип; идентификатор группы (коммуникатора);

  • MPI_COMM_WORLD - зарезервированный идентификатор группы, состоящей их всех процессов приложения.

3.8 Латентность и пропускная способность

3.8.1 Понятия латентности и пропускной способности

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

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

Для улучшения характеристик латентности и пропускной способности необходимо:

  • подсчитать кол-во каналов передачи данных между процессами при разработке крупномодульных приложений;

  • использовать архивацию данных для больших сообщений, а также использовать описываемые типы данных вместо MPI_PACK и MPI_UNPACK если возможно;

  • использовать при возможности коллективные операции; это устраняет вызов MPI_Send и MPI_RECV каждый раз при коммуникации процессов;

  • определять номер принимающего процесса при вызове подпрограммы MPI; использование MPI_ANY_SOURCE может увеличивать латентность;

  • использовать MPI_RECV_INIT и MPI_STARTALL вместо вызова MPI_Irecv в цикле в случаях, когда запросы/прием не могут быть выполнены сразу.


Например, вы написали программу, содержащую фрагмент:

j = 0

for (i=0; i

if (i==rank) continue;

MPI_Irecv(buf[i], count, dtype, i, 0, comm, &requests[j++]);

}

MPI_Waitall(size-1, requests, statuses);


Предположим, что одна из итераций с вызовом MPI_IRECV не завершилась перед следующей итерацией цикла. В этом случае, MPI пробует выполнить оба запроса. Это может продолжаться, приводя к большему времени ожидания. Чтобы избежать этого, можно переписать эту часть кода так:


j = 0

for (i=0; i

if (i==rank) continue;

MPI_Recv_init(buf[i], count, dtype, i, 0, comm,

&requests[j++]);

}

MPI_Startall(size-1, requests);

MPI_Waitall(size-1, requests, statuses);


В этом случае все итерации с вызовом MPI_RECV_INIT выполняются только один раз при вызове MPI_STARTALL. При таком подходе вы не получите дополнительного времени ожидания при использовании MPI_Irecv и может улучшить латентность приложения.

3.8.2 Выбор подпрограмм/функций MPI

Для достижения наименьшей латентности и наибольшей пропускной способности сообщений для синхронной передачи "точка-точка", используйте блокирующие функции MPI MPI_Send и MPI_RECV. Для асинхронной передачи, используйте неблокирующие функции MPI MPI_Isend и MPI_IRECV.

При использовании блокирующих функций, старайтесь избегать ожидающих запросов.

Для задач требующих использования коллективных операций, используют соответствующую коллективную функцию MPI.


4 Входные и выходные данные

4.1 Этапы создания программ

Создание параллельной программы состоит из следующих этапов:

  • последовательный алгоритм подвергается декомпозиции (распараллеливанию), т.е. разбивается на независимо работающие ветви; для взаимодействия в ветви вводятся две дополнительные нематематические операции: прием и передача данных;

  • распараллеленный алгоритм записывается в виде программы, в которой операции приема и передачи записываются в терминах конкретной системы связи между ветвями;

  • полученная таким образом программа компилируется и компонуется с библиотеками среды параллельного программирования при помощи компилятора, используемого на данной системе для получения машинно-зависимого кода.

4.2 Компиляция модулей

Для компиляции и сборки программного модуля, написанного на С, используется команда mpicc. Аналогично для С++ используется команда mpiCC, для Fortran 77 используется mpif77, а для Fortran 90 – mpif90. Все команды пакета MPICH for GM настроены на использование компиляторов фирмы Compaq.

Эти команды предусматривают некоторые опции и подключают специальные библиотеки, необходимые для компиляции и сборки программ MPI:

  • опция указывается для выполнения только компиляции файла, не создавая объектный файл;

  • при задании опции осуществляется сборка и компиляция, а также создается объектный и запускаемый файлы.

Примеры

1 Компиляция программного модуля myprog.c

mpicc –c myprog.c

2 Сборка и компиляция программного модуля myprog.c с созданием выходного файла myfile

mpicc myprog.c –o myfile

4.3 Запуск программ, использующих MPI

Запуск на исполнение MPI-программы производится с помощью команды:

mpirun –np <число_используемых_процессоров> <имя_модуля> [-hmaxtimequantum -stdiodir] <имя_программы> [параметры_программы...]


Параметры команды mpirun слелующие:

  • параметр -h используется для выдачи интерактивной подсказки по параметрам команды mpirun;


  • параметр -np <число_процессоров> обозначает число процессоров, требуемое программе;


  • параметр -maxtime <максимальное_время> задает максимальное время счета. От этого времени зависит положение задачи в очереди. После истечения этого времени задача принудительно заканчивается;


  • параметр -quantum <значение_кванта_времени> указывает, что задача является фоновой, и задает размер кванта для фоновой задачи;


  • параметр -stdiodir <имя_директории> задает имя каталога стандартного вывода, в который будут записываться протокол запуска задачи, файл стандартного вывода и имена модулей, на которых запускалась задача.

Более подробное описание параметров команды запуска задач и постановки задачи в очередь приведено в руководстве “Подсистема коллективного доступа к ресурсам СК” [7].


5 Средства отладки и профилирования

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

5.1 Обработчики ошибок

Стандарт MPI определяет механизм для установки пользовательских обработчиков ошибок и определяет поведение двух встроенных обработчиков: MPI_ERRORS_RETURN и MPI_ERRORS_ARE_FATAL. В библиотеку mpe встроено еще два обработчика ошибок для облегчения использования отладчика dbx с программами, написанными с использованием стандарта MPI:


MPE_Errors_call_dbx_in_xterm

MPE_Signals_call_debugger


Данные обработчики ошибок расположены в директории mpe, основного дерева каталогов MPICH. При конфигурировании MPICH с опцией -mpedbg, эти отладчики включаются в основные библиотеки MPICH, и появляется возможность (с помощью аргумента командной строки mpedbg) установить обработчик MPE_Errors_call_dbx_in_xterm вызываемым по умолчанию обработчиком ошибок (вместо MPI_ERRORS_ARE_FATAL). В приложении А приведен пример программы, обрабатывающей ошибки с помощью процедур MPI (tester.c).

5.2 Профилировочные библиотеки

Профилировочный интерфейс MPE (Message Passing Extensions) представляет собой инструмент для добавления процедур анализа производительности в любую MPI-программу. С пакетом MPICH поставляется три профилировочные библиотеки:

  • библиотека определения времени выполнения процедур MPI;

  • библиотека создания файла журнала и утилита UpShot;

  • библиотека анимации процесса работы программы в реальном времени.

В Приложении А приведен пример программы с использованием профилировочных библиотек cpilog.c.

5.2.1 Библиотека определения времени выполнения процедур MPI

Данная библиотека достаточно проста. Профилировочная версия каждой процедуры MPI (MPI_Xxx) вызывает функцию PMPI_Wtime (возвращающую текущее время) перед и после каждого вызова соответствующей PMPI_Xxx процедуры. Времена накапливаются для каждого процесса и выводятся в файл (отдельный файл для каждого процесса) в профилировочной версии MPI_Finalize. В дальнейшем эти файлы можно использовать для создания отчета по всему приложению или по отдельным процессам. Текущая реализация библиотеки не обрабатывает вложенные циклы.

5.2.2 Создание файла журнала и утилита UpShot

Эта профилировочная библиотека предназначена для генерации файла журнала (log-файла), в котором фиксируются события, привязанные ко времени.

Для сохранения определенных типов событий в памяти, в процессе выполнения вызывается процедура MPI_Log_event, а сборка и объединение этих частей памяти с информацией о событиях происходит в MPI_Finalize. Для остановки и перезапуска операций записи событий во время выполнения программы может использоваться процедура MPI_Pcontrol.

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



Рисунок 1 – Вид файла журнала


5.2.3 Анимация процесса работы программы в реальном времени

Графическая библиотека MPE предоставляет возможности для простой анимации в реальном времени. Библиотека содержит процедуры, которые позволяют разделять X-дисплей нескольким процессам. На основе данной библиотеки существует возможность графически изображать процесс передачи сообщений и их интенсивность в процессе работы программы.

Для сборки программы с использованием графических библиотек MPE при компиляции можно указать опцию –lmpe.

В MPICH предусмотрены также опции для компиляции и сборки программ с различными профилировочными библиотеками MPE:

-mpitrace

Для компиляции и сборки с отладочными библиотеками.

-mpianim

Для компиляции и сборки с анимационными библиотеками.

-mpilog

Для компиляции и сборки с регистрирующими библиотеками .

Пример

mpif77 -mpilog -o fpilog fpilog.f


Более подробную информацию об использовании профилировочных библиотек и о синтаксисе процедур MPE можно найти в документации по МРЕ [3, 1] а также в страницах справочного руководства (man pages).

Для использования отладчиков и профилировочных библиотек необходимо указывать специальные опции при конфигурации пакета MPICH, которые можно найти в п. 3.5 Руководства системного программиста по ОПО СК “МВС-1000М” [8].

5.3 Аргументы командной строки для mpirun

Команда mpirun предоставляет программисту некоторые возможности для облегчения использования отладчика со своей программой.

Команда

mpirun -dbx -np 2 program


начинает выполнение программы на двух машинах, запуская локальную копию программы в отладчике dbx. Опция -gdb позволяет использовать в качестве gdb отладчика, а опция -xxgdb запускает программу в Х Window интерфейсе для gdb – xxgbd.

5.4 Аргументы MPI для программ пользователя

Приведенные ниже аргументы являются недокументированными возможностями MPICH. Некоторые из приведенных аргументов требуют, чтобы MPICH был сконфигурирован и собран со специальными параметрами:

-mpedbg

при возникновении ошибки в программе пользователя, запускает xterm, присоединенный к процессу, вызвавшему ошибку. MPICH должен быть сконфигурирован с опцией -mpedbg. Данный аргумент работает не на всех системах;

-mpiversion

печатает версию MPICH и аргументы, использованные при его конфигурировании;

-mpichdebug

генерирует детальную информацию по каждой производимой MPICH операции;

-mpiqueue

описывает состояние очередей вызова MPI_Finalize. Может быть использован для поиска “потерянных” сообщений.

Данные аргументы указываются программе пользователя, а не команде mpirun. Например,

mpirun -np 2 a.out -mpichdebug

Приложение A

(справочное)

Примеры программ с использованием функций MPI


В данном приложении приведены фрагменты MPI-программ (схематичные примеры) и ряд примеров прикладных программ, использующих MPI.

Перечень прикладных программ представлен в Таблице 2.


Таблица 2

имя файла

язык

комментарий

параметр -np

fpi.f

Fortran 77

Вычисляет число pi, интегрируя f (x) =4 / (1+x2).

np>=1

ctest.c

C

Пример измерения латентности для вызовов процедур MPI.

np=2

cart.c

C

Тест виртуальной топологии

np>1

tester.c

C

Тест обработки ошибок

np>=1

srtest.c

C

Тест пересылки сообщений

np>1

ping_pong.f

Fortran 77

Измеряет время передачи сообщений между двумя процессами

np=2

mpitest.c

C

Тест эффективности основных операций MPI

np>=4

cpilog.c

C

Программа с использованием профилировочных библиотек

np>2


А1 Схематичные примеры MPI-программ

А1.1 Схематичный пример инициализации параллельной части программы, определение кол-ва процессов в группе MPI_COMM_WORLD, где каждый процесс печатает размер группы и свой номер.


main(int argc, char **argv)

{

int me, size;

. . .

MPI_Init (&argc, &argv);

MPI_Comm_rank (MPI_COMM_WORLD, &me);

MPI_Comm_size (MPI_COMM_WORLD, &size);


(void)printf ("Process %d size %d\n", me, size);

. . .

MPI_Finalize();

}


А1.2 Схематичный пример программы, где главный процесс рассылает сообщения остальным процессам, а рабочие процессы группы принимают данные и выводят сообщение об этом.

#include "mpi.h"

main (argc, argv)

int argc;

char **argv;

{

char message[20];

int myrank;

MPI_Status status;

MPI_Init (&argc, &argv);

MPI_Comm_rank (MPI_COMM_WORLD, &myrank);

if (myrank==0) /* код для процесса 0*/

{

strcpy (message, "Hello, there");


MPI_Send(messege, strlen(messege), MPI_CAHR, 1, 99, MPI_COMM_WORLD);

}

else /* код для процесса 1 */

{

MPI_Recv (message, 20, MPI_CHAR, 0, 99, MPI_COMM_WORLD, &status);

printf ("receiveds :%s:\n", message);

}

MPI_Finalize();

}


А1.3 Схематичный пример программы, демонстрирующий обмен сообщениями между процессами группы с помощью процедур MPI_Send и MPI_Recv

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); /* local */

MPI_Comm-size (MPI_COMM_WORLD, &size); /* local */

if (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();

}


А2 Тест виртуальных топологий (cart.c)

Это программа на С, генерирующая виртуальную топологию. При компиляции этого примера необходимо подключить (указать путь) файл test.h, который входит в стандартные примеры MPICH и находится в директории /common/mpich/examples/test/topol

#include "mpi.h"

#include

#include "test.h"

#define NUM_DIMS 2

int main( int argc, char **argv )

{

int rank, size, i;

int errors=0;

int dims[NUM_DIMS];

int periods[NUM_DIMS];

int coords[NUM_DIMS];

int new_coords[NUM_DIMS];

int reorder = 1;

MPI_Comm comm_temp, comm_cart, new_comm;

int topo_status;

int ndims;

int new_rank;

int remain_dims[NUM_DIMS];

int newnewrank;

MPI_Init( &argc, &argv );

MPI_Comm_rank( MPI_COMM_WORLD, &rank );

MPI_Comm_size( MPI_COMM_WORLD, &size );

/* Обнуление массива сетки и создание топологии */

for(i=0;i

MPI_Dims_create ( size, NUM_DIMS, dims );

/* Создание нового коммуникатора для топологии */

MPI_Cart_create ( MPI_COMM_WORLD, 2, dims, periods, reorder, &comm_temp );

MPI_Comm_dup ( comm_temp, &comm_cart );

/* Определение состояния нового коммуникатора */

MPI_Topo_test ( comm_cart, &topo_status );

if (topo_status != MPI_CART) errors++;

/* Определение кол-ва измерений сетки в топологии */

MPI_Cartdim_get( comm_cart, &ndims );

if ( ndims != NUM_DIMS ) errors++;

/* Проверка корректности полученной топологии */

for(i=0;i

MPI_Cart_get ( comm_cart, NUM_DIMS, dims, periods, coords );

/* Проверка соответствия координат процесса в сетке топологии номеру процесса */

MPI_Cart_rank ( comm_cart, coords, &new_rank );

if ( new_rank != rank ) errors++;

/* Проверка соответствия номера процесса координатам процесса в сетке топологии */

MPI_Cart_coords ( comm_cart, rank, NUM_DIMS, new_coords );

for (i=0;i

if ( coords[i] != new_coords[i] )

errors++;

/* Сдвиг в каждом измерении сетки топологии и проверка ее работы*/

for (i=0;i

int source, dest;

MPI_Cart_shift(comm_cart, i, 1, &source, &dest);

#ifdef VERBOSE

printf ("[%d] Shifting %d in the %d dimension\n",rank,1,i);

printf ("[%d] source = %d dest = %d\n",rank,source,dest);

#endif

}

/* Выделение подгрупп коммуникатора для подсеток топологии */

remain_dims[0] = 0;

for (i=1; i

MPI_Cart_sub ( comm_cart, remain_dims, &new_comm );

/* Определение статуса нового коммуникатора */

MPI_Topo_test ( new_comm, &topo_status );

if (topo_status != MPI_CART) errors++;

/* Определение кол-ва измерений сетки в топологии */

MPI_Cartdim_get( new_comm, &ndims );

if ( ndims != NUM_DIMS-1 ) errors++;

/* Проверка корректности полученной топологии */

for(i=0;i

MPI_Cart_get ( new_comm, ndims, dims, periods, coords );

/* Проверка соответствия координат процесса в сетке топологии номеру процесса */

MPI_Comm_rank ( new_comm, &newnewrank );

MPI_Cart_rank ( new_comm, coords, &new_rank );

if ( new_rank != newnewrank ) errors++;

/* Проверка соответствия номера процесса координатам процесса в сетке топологии */

MPI_Cart_coords ( new_comm, new_rank, NUM_DIMS -1, new_coords );

for (i=0;i

if ( coords[i] != new_coords[i] )

errors++;

/* Завершение программы */

MPI_Comm_free( &new_comm );

MPI_Comm_free( &comm_temp );

MPI_Comm_free( &comm_cart );

Test_Waitforall( );

if (errors) printf( "[%d] done with %d ERRORS!\n", rank,errors );

MPI_Finalize();

return 0;

}


Вывод результата работы программы для np=2

call_batch: calling batch

All processes completed test

Вывод результата работы программы для np=32

call_batch: calling batch

All processes completed test

А3 Тест пересылки сообщений (srtest.c)

Эта программа выдает сообщения о коммуникациях в группе.

#include "mpi.h"

#include

#define BUFLEN 512

int main(argc,argv)

int argc;

char *argv[];

{

int i, myid, numprocs, next, rc, namelen;

char buffer[BUFLEN], processor_name[MPI_MAX_PROCESSOR_NAME];

MPI_Status status;

MPI_Init(&argc,&argv);

MPI_Comm_size(MPI_COMM_WORLD,&numprocs);

MPI_Comm_rank(MPI_COMM_WORLD,&myid);

MPI_Get_processor_name(processor_name,&namelen);

fprintf(stderr,"Process %d on %s\n", myid, processor_name);

strcpy(buffer,"hello there");

if (myid == numprocs-1)

next = 0;

else

next = myid+1;

if (myid == 0)

{

printf("%d sending '%s' \n",myid,buffer);

MPI_Send(buffer, strlen(buffer)+1, MPI_CHAR, next, 99, MPI_COMM_WORLD);

printf("%d receiving \n",myid);

MPI_Recv(buffer, BUFLEN, MPI_CHAR, MPI_ANY_SOURCE, 99, MPI_COMM_WORLD,

&status);

printf("%d received '%s' \n",myid,buffer);

}

else

{

printf("%d receiving \n",myid);

MPI_Recv(buffer, BUFLEN, MPI_CHAR, MPI_ANY_SOURCE, 99, MPI_COMM_WORLD,

&status);

printf("%d received '%s' \n",myid,buffer);

MPI_Send(buffer, strlen(buffer)+1, MPI_CHAR, next, 99, MPI_COMM_WORLD);

printf("%d sent '%s' \n",myid,buffer);

}

MPI_Barrier(MPI_COMM_WORLD);

MPI_Finalize();

}


Вывод результата работы программы для np=2

call_batch: calling batch

1 receiving

0 sending 'hello there'

0 receiving

0 received 'hello there'

1 received 'hello there'

1 sent 'hello there'

Вывод результата работы программы для np=32

call_batch: calling batch

20 receiving

30 receiving

8 receiving

13 receiving

10 receiving

16 receiving

2 receiving

4 receiving

6 receiving

9 receiving

7 receiving

12 receiving

3 receiving

14 receiving

21 receiving

11 receiving

17 receiving

18 receiving

19 receiving

5 receiving

15 receiving

24 receiving

22 receiving

26 receiving

23 receiving

28 receiving

27 receiving

25 receiving

29 receiving

31 receiving

1 receiving

0 sending 'hello there'

20 received 'hello there'

20 sent 'hello there'

30 received 'hello there'

30 sent 'hello there'

8 received 'hello there'

8 sent 'hello there'

18 received 'hello there'

18 sent 'hello there'

13 received 'hello there'

13 sent 'hello there'

10 received 'hello there'

10 sent 'hello there'

21 received 'hello there'

21 sent 'hello there'

16 received 'hello there'

16 sent 'hello there'

4 received 'hello there'

4 sent 'hello there'

2 received 'hello there'

2 sent 'hello there'

6 received 'hello there'

6 sent 'hello there'

12 received 'hello there'

12 sent 'hello there'

11 received 'hello there'

11 sent 'hello there'

9 received 'hello there'

9 sent 'hello there'

14 received 'hello there'

14 sent 'hello there'

19 received 'hello there'

19 sent 'hello there'

22 received 'hello there'

22 sent 'hello there'

28 received 'hello there'

28 sent 'hello there'

25 received 'hello there'

25 sent 'hello there'

27 received 'hello there'

27 sent 'hello there'

31 received 'hello there'

31 sent 'hello there'

3 received 'hello there'

3 sent 'hello there'

7 received 'hello there'

7 sent 'hello there'

17 received 'hello there'

17 sent 'hello there'

26 received 'hello there'

26 sent 'hello there'

23 received 'hello there'

23 sent 'hello there'

24 received 'hello there'

24 sent 'hello there'

29 received 'hello there'

29 sent 'hello there'

5 received 'hello there'

5 sent 'hello there'

15 received 'hello there'

15 sent 'hello there'

0 receiving

0 received 'hello there'

1 received 'hello there'

1 sent 'hello there'


А4 Вычисление числа Pi ( fpi.f )

Это пример программы на Fortran 77, который вычисляет число pi интегрируя f(x) = 4/(1 + x2).

Каждый процесс:

  • получает число интервалов, используемых в приближении;

  • вычисляет в них интегралы;

  • синхронизирует для общего суммирования.

Программа выдает результат вычислений, погрешность и время вычисления.

program main

include 'mpif.h'

double precision PI25DT

parameter (PI25DT = 3.141592653589793238462643d0)

double precision mypi, pi, h, sum, x, f, a

integer n, myid, numprocs, i, rc

c Интегрируемая функция

f(a) = 4.d0 / (1.d0 + a*a)

call MPI_INIT( ierr )

call MPI_COMM_RANK( MPI_COMM_WORLD, myid, ierr )

call MPI_COMM_SIZE( MPI_COMM_WORLD, numprocs, ierr )

print *, "Process ", myid, " of ", numprocs, " is alive"

sizetype = 1

sumtype = 2

n = 0

10 if ( myid .eq. 0 ) then

write(6,98)

98 format('Enter the number of intervals: (0 quits)')

read(5,99) n

99 format(i10)

c if ( n .eq. 0 ) then

c n = 100

c else

c n = 0

c endif

print *, "got input n =", n

endif

call MPI_BCAST(n,1,MPI_INTEGER,0,MPI_COMM_WORLD,ierr)

c проверка принятия сообщения

if ( n .le. 0 ) goto 30

c вычисление размера интервала

h = 1.0d0/n

sum = 0.0d0

do 20 i = myid+1, n, numprocs

x = h * (dble(i) - 0.5d0)

sum = sum + f(x)

20 continue

mypi = h * sum

c прием всех частичных сумм

call MPI_REDUCE(mypi,pi,1,MPI_DOUBLE_PRECISION,MPI_SUM,0,

$ MPI_COMM_WORLD,ierr)

c печать ответа в 0-й ветви.

if (myid .eq. 0) then

write(6, 97) pi, abs(pi - PI25DT)

97 format(' pi is approximately: ', F18.16,

+ ' Error is: ', F18.16)

endif

goto 10

30 call MPI_FINALIZE(rc)

stop

end



Вывод результата работы программы для np=2

call_batch: calling batch

Process 1 of 2 is alive

Process 0 of 2 is alive

Enter the number of intervals: (0 quits)


Вывод результата работы программы для np=32

call_batch: calling batch

Process 29 of 32 is alive

Process 5 of 32 is alive

Process 2 of 32 is alive

Process 11 of 32 is alive

Process 4 of 32 is alive

Process 3 of 32 is alive

Process 6 of 32 is alive

Process 7 of 32 is alive

Process 8 of 32 is alive

Process 13 of 32 is alive

Process 9 of 32 is alive

Process 10 of 32 is alive

Process 12 of 32 is alive

Process 15 of 32 is alive

Process 14 of 32 is alive

Process 16 of 32 is alive

Process 19 of 32 is alive

Process 17 of 32 is alive

Process 18 of 32 is alive

Process 20 of 32 is alive

Process 21 of 32 is alive

Process 22 of 32 is alive

Process 23 of 32 is alive

Process 24 of 32 is alive

Process 26 of 32 is alive

Process 25 of 32 is alive

Process 27 of 32 is alive

Process 28 of 32 is alive

Process 30 of 32 is alive

Process 31 of 32 is alive

Process 1 of 32 is alive

Process 0 of 32 is alive

Enter the number of intervals: (0 quits)

A5 Измерение латентности вызовов процедур MPI (ctest.c)

Это пример программы на C , где латентность вызовов процедур MPI находится путем измерения разницы между временем вызова процедуры и результатами времени в эталонном тесте Dongarra.

#include "mpi.h"

#include

#include

#define MAX_TIMES 16386

static double times[MAX_TIMES];

int main( argc, argv )

int argc;

char **argv;

{

int i;

int ntest = MAX_TIMES;

double minsep, maxsep, avesep, sep, sd, deltasep, mult;

int citoutput = 1, nmatch;

MPI_Init( &argc, &argv );

/* Установление счетчиков времени */

for (i=0; i

times[i] = MPI_Wtime();

/* Получение начальных данных. При этом не измеряется время неудачных обращений к кэшу команды, занимающее дополнительное время */

for (i=0; i

times[i] = MPI_Wtime();

/* Просмотр вариантов */

minsep = 1.0e6;

maxsep = 0.0;

avesep = 0.0;

for (i=1; i

sep = times[i] - times[i-1];

if (sep < minsep) minsep = sep;

if (sep > maxsep) maxsep = sep;

avesep += sep;

}

avesep /= (ntest-1);

/* C Вычисление среднеквадратичного отклонение разниц относительно устойчивым способом */

sd = 0.0;

for (i=1; i

sep = times[i] - times[i-1];

sd += (sep - avesep) * (sep - avesep);

}

/* Масштаб в мкс */

sd *= 1.0e+12;

sd = sqrt(sd) / (ntest - 2);

/* Интересный вопрос - - все ли (или большинство) разниц являются множителями minsep. Сначала находят deltasep (время между первым и вторым различными измерениями.

*/

deltasep = maxsep;

for (i=1; i

sep = times[i] - times[i-1];

if (sep > minsep && sep < deltasep) deltasep = sep;

}

deltasep -= minsep;

/* Далее находят кол-во разниц, являющихся множителями deltasep */

nmatch = 0;

for (i=1; i

sep = times[i] - times[i-1];

mult = (sep - minsep) / deltasep;

if (fabs( mult - (int)mult) < 0.05) nmatch++;

}

/* Печать результатов */

printf( "#Variance in clock:\n\

#Minimum time between calls: %6.2f usec\n\

#Maximum time between calls: %6.2f usec\n\

#Average time between calls: %6.2f usec\n\

#Standard deviation: %12.3e\n",

minsep * 1.e6, maxsep * 1.e6, avesep * 1.e6, sd );

if (nmatch > ntest/4) {

printf( "#Apparent resolution of clock is: %6.2f usec\n",

deltasep * 1.0e6 );

}

printf(

"# This program should be run multiple times for better understanding\n" );

/* Print C.It graphics stuff if requested */

if (citoutput) {

for (i=1; i

sep = times[i] - times[i-1];

printf( "%f\n", sep * 1.0e6 );

}

printf( "hist\nwait\nnew page\n" );

}

MPI_Finalize();

return 0;

}



Вывод результата работы программы для np=2

call_batch: calling batch

#Variance in clock:

#Variance in clock:

#Minimum time between calls: 0.00 usec

#Maximum time between calls: 978.11 usec

#Average time between calls: 1.37 usec

#Standard deviation: 2.856e-01

#Apparent resolution of clock is: 0.95 usec

# This program should be run multiple times for better understanding

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

…………………

975.966454

2.026558

…………………

0.000000

0.000000

…………………


Вывод результата работы программы для np=32

call_batch: calling batch

#Variance in clock:

#Minimum time between calls: 0.00 usec

#Maximum time between calls: 977.99 usec

#Average time between calls: 0.84 usec

#Standard deviation: 2.230e-01

#Apparent resolution of clock is: 0.95 usec

# This program should be run multiple times for better understanding

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

0.000000

…………………

0.000000

#Minimum time between calls: 0.00 usec

#Maximum time between calls: 978.11 usec

#Average time between calls: 1.01 usec

#Standard deviation: 2.457e-01

#Apparent resolution of clock is: 0.95 usec

# This program should be run multiple times for better understanding

0.000000

0.000000

0.000000

…………………



A6 Тест обработки ошибок (testerr.c)

Эта программа показывает работу процедур для обработки ошибок.

#include <stdio.h>

#include

void Test_Send( void );

void Test_Recv( void );

void Test_Datatype( void );

void Test_Errors_warn( MPI_Comm *comm, int *code, ... )

{

char buf[MPI_MAX_ERROR_STRING+1];

int myid, result_len;

static int in_handler = 0;

if (in_handler) return;

in_handler = 1;

/* Преобразование кода ошибки в сообщение и печать */

MPI_Error_string( *code, buf, &result_len );

printf( "%s\n", buf );

in_handler = 0;

}

static int errcount = 0;

void Test_Failed( char * msg )

{

printf( "FAILED: %s\n", msg );

errcount++;

}

void Test_Passed( char * msg )

{

printf( "Passed: %s\n", msg );

}

int main( int argc, char *argv[] )

{

MPI_Errhandler TEST_ERRORS_WARN;

MPI_Init( &argc, &argv );

MPI_Errhandler_create( Test_Errors_warn, &TEST_ERRORS_WARN );

MPI_Errhandler_set(MPI_COMM_WORLD, TEST_ERRORS_WARN);

Test_Send();

Test_Recv();

Test_Datatype();

MPI_Finalize();

return 0;

}

void Test_Send( void )

{

int buffer[100];

int dest;

MPI_Datatype bogus_type = MPI_DATATYPE_NULL;

MPI_Status status;

int myrank, size;

int large_tag, flag, small_tag;

int *tag_ubp;

MPI_Comm_rank( MPI_COMM_WORLD, &myrank );

MPI_Comm_size( MPI_COMM_WORLD, &size );

dest = size - 1;

if (MPI_Send(buffer, 20, MPI_INT, dest,

1, MPI_COMM_NULL) == MPI_SUCCESS){

Test_Failed("NULL Communicator Test");

}

else

Test_Passed("NULL Communicator Test");

if (MPI_Send(buffer, -1, MPI_INT, dest,

1, MPI_COMM_WORLD) == MPI_SUCCESS){

Test_Failed("Invalid Count Test");

}

else

Test_Passed("Invalid Count Test");

if (MPI_Send(buffer, 20, bogus_type, dest,

1, MPI_COMM_WORLD) == MPI_SUCCESS){

Test_Failed("Invalid Type Test");

}

else

Test_Passed("Invalid Type Test");

small_tag = -1;

if (small_tag == MPI_ANY_TAG) small_tag = -2;

if (MPI_Send(buffer, 20, MPI_INT, dest,

small_tag, MPI_COMM_WORLD) == MPI_SUCCESS) {

Test_Failed("Invalid Tag Test");

}

else

Test_Passed("Invalid Tag Test");

/* Тег сообщения слишком велик */

MPI_Attr_get( MPI_COMM_WORLD, MPI_TAG_UB, (void **)&tag_ubp, &flag );

if (!flag) Test_Failed("Could not get tag ub!" );

large_tag = *tag_ubp + 1;

if (large_tag > *tag_ubp) {

if (MPI_Send(buffer, 20, MPI_INT, dest,

-1, MPI_COMM_WORLD) == MPI_SUCCESS) {

Test_Failed("Invalid Tag Test");

}

else

Test_Passed("Invalid Tag Test");

}

if (MPI_Send(buffer, 20, MPI_INT, 300,

1, MPI_COMM_WORLD) == MPI_SUCCESS) {

Test_Failed("Invalid Destination Test");

}

else

Test_Passed("Invalid Destination Test");

if (MPI_Send((void *)0, 10, MPI_INT, dest,

1, MPI_COMM_WORLD) == MPI_SUCCESS){

Test_Failed("Invalid Buffer Test (send)");

}

else

Test_Passed("Invalid Buffer Test (send)");

}

void Test_Recv( void )

{

}

void Test_Datatype( void )

{

}

#ifdef FOO

void

ReceiverTest3()

{

int buffer[20];

MPI_Datatype bogus_type = MPI_DATATYPE_NULL;

MPI_Status status;

int myrank;

int *tag_ubp;

int large_tag, flag, small_tag;

MPI_Comm_rank( MPI_COMM_WORLD, &myrank );

if (myrank == 0) {

fprintf( stderr,

"There should be eight error messages about invalid communicator\n\

count argument, datatype argument, tag, rank, buffer send and buffer recv\n" );

}

/* Тест посылки сообщений может не выдавать ошибок, пока он не запущен */

if (MPI_Recv((void *)0, 10, MPI_INT, src,

15, MPI_COMM_WORLD, &status) == MPI_SUCCESS){

Test_Failed("Invalid Buffer Test (recv)");

}

else

Test_Passed("Invalid Buffer Test (recv)");

/* Прием посылаеммого сообщения */

{ int flag, ibuf[10];

MPI_Iprobe( src, 15, MPI_COMM_WORLD, &flag, &status );

if (flag)

MPI_Recv( ibuf, 10, MPI_INT, src, 15, MPI_COMM_WORLD, &status );

}

return;

#endif


Вывод результата работы программы для np=2

call_batch: calling batch

Null communicator

Null communicator

Passed: NULL Communicator Test

Invalid count argument is -1

Passed: Invalid Count Test

Datatype is MPI_TYPE_NULL

Passed: Invalid Type Test

Invalid message tag -2

Passed: Invalid Tag Test

Invalid message tag -1

Passed: Invalid Tag Test

Invalid rank 300

Passed: Invalid Destination Test

Invalid buffer pointer

Passed: Invalid Buffer Test (send)

Passed: NULL Communicator Test

Invalid count argument is -1

Passed: Invalid Count Test

Datatype is MPI_TYPE_NULL

Passed: Invalid Type Test

Invalid message tag -2

Passed: Invalid Tag Test


Вывод результата работы программы для np=32

call_batch: calling batch

Null communicator

Null communicator

Null communicator

Null communicator

Null communicator

Null communicator

Null communicator

Null communicator

Null communicator

Null communicator

Null communicator

Null communicator

Null communicator

Null communicator

Passed: NULL Communicator Test

Invalid count argument is -1

Passed: Invalid Count Test

Datatype is MPI_TYPE_NULL

Passed: Invalid Type Test

Invalid message tag -2

Passed: Invalid Tag Test

Invalid message tag –1

Passed: Invalid Tag Test

Invalid rank 300

Passed: Invalid Destination Test

Invalid buffer pointer

Passed: Invalid Buffer Test (send)

Null communicator

Passed: NULL Communicator Test

Invalid count argument is -1

Passed: Invalid Count Test

Datatype is MPI_TYPE_NULL

Passed: Invalid Type Test

Invalid message tag -2

Passed: Invalid Tag Test

Invalid message tag -1

Passed: Invalid Tag Test

Invalid rank 300

Passed: Invalid Destination Test

Invalid buffer pointer

……………………………………………………

A7 Обмен сообщениями (ping_pong.f)

Этот пример измеряет скорость пересылки сообщений.


PROGRAM PingPong

INCLUDE 'mpif.h'

integer p

integer my_rank

integer test

integer min_size

integer max_size

integer incr

integer size

parameter (min_size = 0, max_size = 10000, incr = 200)

parameter (size = 80000)

double precision x(0:size)

integer pass

integer status(MPI_STATUS_SIZE)

integer ierr

integer i

double precision wtime_overhead

double precision start, finish,timex

double precision raw_time, cumulativ,y,tmin

double precision dtime(0:127,0:127),delta,tmax,tavg

double precision tmax2,tmin2,tavg2

integer comm, count(0:127)

integer MAX_ORDER, MAX

parameter (MAX_ORDER = 1000, MAX = 8)

character*23 nodes(0:127),dummy

character*4 pname(0:127), myname

C

call MPI_INIT( ierr)

call MPI_COMM_SIZE(MPI_COMM_WORLD, p, ierr )

call MPI_COMM_RANK(MPI_COMM_WORLD, my_rank, ierr )

call MPI_COMM_DUP(MPI_COMM_WORLD, comm, ierr )

call MPI_GET_PROCESSOR_NAME(myname,len,ierr)

call MPI_GATHER(myname,4,MPI_CHARACTER,pname,4,

+ MPI_CHARACTER,0,MPI_COMM_WORLD,ierr)

call MPI_BCAST(pname,512,MPI_CHARACTER,0,MPI_COMM_WORLD,ierr)

if(my_rank.eq.0) print*,"Starting..."

C

C

y=4.D0*DATAN(1.D0)

do 9 i=1,size

x(i)=y

9 continue

noff=mod(p,2)

C mynoff=mod(myrank,2)

if(noff.eq.1) then

print*, "Error: odd number of processes..."


Случайные файлы

Файл
20721-1.rtf
9035-1.rtf
33885.rtf
118686.rtf
16943-1.rtf