Следующие пункты обычно используемых параметров для команды mpirun
-np определяет число задач на выполнение
-wd определяет рабочую директорию для использования процессом MPI. Это необходимо только тогда, когда текущая директория и желаемая рабочая директория не совпадают.
-h | -help показывает аргументы командной строки для mpirun
-mpi_debug включает дополнительную проверку на входные параметры и дополнительный выход если ошибки возникнут при выполнении.
Хотя MPI обеспечивает расширенное множество вызовов, функциональная программа на MPI может быть записана с помощью всего шести базовых вызова:
MPI_Init
MPI_Comm_rank
MPI_Comm_size
MPI_Send
MPI_Recv
MPI_Finalize
Для удобства программирования и оптимизации кода, вам следует рассмотреть использование других вызовов, таких как описано в модулях по попарной и коллективной коммуникациям.
Сообщения MPI
Сообщения MPI состоят из двух частей:
данные (старт буфера, число, тип данных)
оболочка (назначение/источник, тег, коммуникатор)
Данные определяют информацию, которая будет отослана или получена. Оболочка (конверт) используется в маршрутизации сообщения к получателю и связывает вызовы отправки с вызовами получения.
Коммуникаторы
Коммуникаторы гарантируют уникальные пространства сообщений. В соединении с группами процессов их можно использовать, чтобы ограничить коммуникацию к подмножеству процессов.
Все дополнительные объекты: имена функций, константы, предопределенные типы данных и т.п., используемые в MPI, имеют префикс MPI_. Если пользователь не будет использовать в программе имен с таким префиксом, то конфликтов с объектами MPI заведомо не будет. Все описания интерфейса MPI собраны в файле mpi.h, поэтому в начале MPI-программы должна стоять директива
#include <mpi.h>
MPI-программа — это множество параллельных взаимодействующих процессов. Все процессы порождаются один раз, образуя параллельную часть программы. В ходе выполнения MPI-программы порождение дополнительных процессов или уничтожение существующих не допускается. Каждый процесс работает в своем адресном пространстве, никаких общих переменных или данных в MPI нет. Основным способом взаимодействия между процессами является явная посылка сообщений.
Для локализации взаимодействия параллельных процессов программы можно создавать группы процессов, предоставляя им отдельную среду для общения — коммуникатор. Состав образуемых групп произволен. Группы могут полностью входить одна в другую, не пересекаться или пересекаться частично. При старте программы всегда считается, что все порожденные процессы работают в рамках всеобъемлющего коммуникатора, имеющего предопределенное имя mpi_comm_world. Этот коммуникатор существует всегда и служит для взаимодействия всех процессов MPI-программы.
Каждый процесс MPI-программы имеет уникальный атрибут номер процесса, который является целым неотрицательным числом. С помощью этого атрибута происходит значительная часть взаимодействия процессов между собой. Ясно, что в одном и том же коммуникаторе все процессы имеют различные номера. Но поскольку процесс может одновременно входить в разные коммуникаторы, то его номер в одном коммуникаторе может отличаться от его номера в другом. Отсюда становятся понятными два основныю атрибута процесса: коммуникатор и номер в коммуникаторе. Если группа содержит n процессов, то номер любого процесса в данной группе лежит в пределах от 0 до n - 1.
Основным способом общения процессов между собой является посылка сообщений. Сообщение — это набор данных некоторого типа. Каждое сообщение имеет несколько атрибутов, в частности, номер процесса-отправителя, номер процесса-получателя, идентификатор сообщения и другие. Одним из важных атрибутов сообщения является его идентификатор или тэг. По идентификатору процесс, принимающий сообщение, например, может различить два сообщения, пришедшие к нему от одного и того же процесса. Сам идентификатор сообщения является целым неотрицательным числом, лежащим в диапазоне от 0 до 32767. Для работы с атрибутами сообщений введена структура MPI_Status, поля которой дают доступ к их значениям.
Общие функции MPI
Прежде чем переходить к описанию конкретных функций, сделаем несколько общих замечаний. При описании функций мы всегда будем пользоваться словом OUT для обозначения выходных параметров, через которые функция возвращает результаты. Даже если результатом работы функции является одно число, оно будет возвращено через один из параметров. Связано это с тем, что практически все функции MPI возвращают в качестве своего значения информацию об успешности завершения. В случае успешного выполнения функция вернет значение MPI_SUCCESS, иначе — код ошибки. Вид ошибки, которая произошла при выполнении функции, можно будет понять из описания каждой функции. Предопределенные возвращаемые значения, соответствующие различным ошибочным ситуациям, определены в файле mpi.h. В дальнейшем при описании конкретных функций, если ничего специально не сказано, то возвращаемое функцией значение будет подчиняться именно этому правилу.
В данном разделе мы остановимся на общих функциях MPI, необходимых практически в каждой программе.
int MPI_Init(int *argc, char ***argv)
Инициализация параллельной части программы. Все другие функции MPI могут быть вызваны только после вызова MPI_Init. Необычный тип аргументов MPI_Init предусмотрен для того, чтобы иметь возможность передать всем процессам аргументы функции main. Инициализация параллельной части для каждого приложения должна выполняться только один раз.
int MPI_Finalize(void)
Завершение параллельной части приложения. Все последующие обращения к любым MPI-функциям, в том числе к MPI_Init, запрещены. К моменту вызова MPI_Finalize каждым процессом программы все действия, требующие его участия в обмене сообщениями, должны быть завершены.
Общая схема MPI-программы выглядит так:
main(int argc, char **argv)
{
…
MPI_Init(&argc, &argv);
…
MPI_Finalize();
…
}
int MPI_Comm_size(MPI_Comm comm, int *size)
• comm — идентификатор коммуникатора,
• out size — число процессов в коммуникаторе comm.
Определение общего числа параллельных процессов в коммуникаторе comm. Результат возвращается через параметр size, для чего функции передается адрес этой переменной. Поскольку коммуникатор является сложной структурой, перед ним стоит имя предопределенного типа MPI_Comm, определенного в файле mpi.h.
int MPI_Comm_rank(MPI_Comm comm, int *rank)
• comm — идентификатор коммуникатора,
• out rank — номер процесса в коммуникаторе comm.
Определение номера процесса в коммуникаторе comm. Если функция MPI_Comm_size для того же коммуникатора comm вернула значение size, то значение, возвращаемое функцией MPI_Comm_rank через переменную rank, лежит в диапазоне от 0 до size-1.
double MPI_Wtime(void)
Эта функция возвращает астрономическое время в секундах (вещественное число), прошедшее с некоторого момента в прошлом. Если некоторый участок программы окружить вызовами данной функции, то разность возвращаемых значений покажет время работы данного участка. Гарантируется, что момент времени, используемый в качестве точки отсчета, не будет изменен за время существования процесса. Заметим, что эта функция возвращает результат своей работы не через параметры, а явным образом.
Простейший пример программы, в которой использованы описанные выше функции, выглядит так:
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);
printf("Process %d size %d \n", me, size);
…
MPI_Finalize();
…
}
Строка, соответствующая функции printf, будет выведена столько раз, сколько процессов было порождено при вызове MPIInit. Порядок появления строк заранее не определен и может быть, вообще говоря, любым. Гарантируется только то, что содержимое отдельных строк не будет перемешано друг с другом.