Наиболее распространенной технологией программирования для параллельных систем с распределенной памятью в настоящее время является MPI (Message Passing Interface). Основным способом взаимодействия параллельных процессов друг с другом в таких системах является передача сообщений (Message Passing). По сути MPI – это библиотека и среда исполнения для параллельных программ на языках C или Fortran. В данном пособии будут описаны примеры программ на языке С.
Изначально MPI позволяет использовать модель программирования MIMD (Multiple Instruction Multiple Data) – много потоков инструкций и данных, т.е. объединение различных программ с различными данными. Но программирование для такой модели на практике оказывается слишком сложным, поэтому обычно используется модель SIMD (Single Program Multiple Data) –одна программа и много потоков данных. Здесь параллельная программа пишется так, чтобы разные ее части могли одновременно выполнять свою часть задачи, таким образом, достигается параллелизм. Поскольку все функции MPI содержаться в библиотеке, то при компиляции параллельной программы необходимо будет прилинковать соответствующие модули.
Многопоточность | Потоки | thread | Многопоточное программирование | Уроки | C++ #1
Под параллельной программой в рамках MPI понимается множество одновременно выполняемых процессов. Процессы могут выполняться на разных процессорах, но на одном процессоре могут располагаться и несколько процессов (в этом случае их исполнение осуществляется в режиме разделения времени). При запуске MPI – программы на кластере на каждом из его узлов будет выполняться своя копия программы, выполняющая свою часть задачи, из этого следует, что параллельная программа – это множество взаимодействующих процессов, каждый из которых работает в своем адресном пространстве. В предельном случае для выполнения параллельной программы может использоваться один процессор — как правило, такой способ применяется для начальной проверки правильности параллельной программы.
Количество процессов и число используемых процессоров определяется в момент запуска параллельной программы средствами среды исполнения MPI — программ и в ходе вычислений меняться не может. Все процессы программы последовательно перенумерованы от 0 до np-1, где np есть общее количество процессов. Номер процесса называется рангом процесса.
Взаимодействуют параллельные процессы между собой при помощи посылки сообщений. Методы посылки (их называют коммуникации) бывают двух видов – коллективные(collective) и “точка-точка” (point-to-point).
При коллективных коммуникациях процесс посылает нужную информацию одновременно целой группе процессов, еще есть более общий случай, когда внутри группы процессов передача информации идет от каждого процесса к каждому. Более простыми коммуникациями являются коммуникации типа ”точка-точка”, когда один процесс посылает информацию второму или они оба обмениваются информацией.
Функции коммуникаций – основные функции библиотеки MPI. Кроме этого, обязательными функциями являются функции инициализации и завершения MPI – MPI_Init и MPI_Finalize. MPI_Init должна вызываться в самом начале программ, а MPI_Finalize – в самом конце. Все остальные функции MPI должны вызываться между этими двумя функциями.
Цель в жизни. Как понять? Узнай как достичь любой цели в современном мире! Аудиокнига целиком
Как процесс узнает о том, какую часть вычислений он должен выполнять? Каждый процесс, исполняющийся на кластере, имеет свой уникальный номер – ранг. Когда процесс узнает свой ранг и общее количество процессов, он может определить свою часть работы. Для этого в MPI существуют специальные функции – MPI_Comm_rank и MPI_Comm_size. MPI_Comm_rank возвращает целое число — ранг процесса, вызвавшего ее, а MPI_Comm_size возвращает общее число работающих процессов.
Процессы отлаживаемой параллельной программы пользователя объединяются в группы. Под коммуникатором в MPI понимается специально создаваемый служебный объект, объединяющий в своем составе группу процессов и ряд дополнительных параметров (контекст), используемых при выполнении операций передачи данных.
Коммуникатор, автоматически создаваемый при запуске программы и включающий в себя все процессы на кластере, называется MPI_COMM_WORLD. В ходе вычислений могут создаваться новые и удаляться существующие группы процессов и коммуникаторы. Один и тот же процесс может принадлежать разным группам и коммуникаторам. Коллективные операции применяются одновременно для всех процессов коммуникатора, поэтому для них одним из параметров всегда будет выступать коммуникатор.
При выполнении операций передачи сообщений в функциях MPI необходимо указывать тип пересылаемых данных. MPI содержит большой набор базовых типов данных, основанных на стандартных типах данных языка С. Кроме того, программист может конструировать свои типы данных при помощи специальных функций MPI. Ниже приведена таблица соответствия для базовых типов данных.
Константы MPI | ТИП данных языка С |
MPI_INT | signed int |
MPI_UNSIGNED | unsigned int |
MPI_SHORT | signed int |
MPI_LONG | signed long int |
MPI_UNSIGNED_SHORT | unsigned int |
MPI_UNSIGNED_LONG | unsigned long int |
MPI_FLOAT | float |
MPI_DOUBLE | double |
MPI_LONG_DOUBLE | long double |
MPI_UNSIGNED_CHAR | unsigned char |
MPI_CHAR | signed char |
Пример запуска библиотеки MPI: логин student, пароль s304.
int main (int argc, char *argv[])
/* получение ранга процесса */
Источник: poznayka.org
Общие процедуры MPI
Аннотация: В данной лекции остановимся на общих процедурах MPI, не связанных с пересылкой данных. Большинство процедур, описанных в этой лекции, необходимы практически в каждой содержательной параллельной программе
В данном разделе мы остановимся на общих процедурах MPI , не связанных с пересылкой данных. Большинство процедур этого раздела необходимы практически в каждой содержательной параллельной программе .
MPI_INIT(IERR) INTEGER IERR
Инициализация параллельной части программы. Все другие процедуры MPI могут быть вызваны только после вызова MPI_INIT . Инициализация параллельной части для каждого приложения должна выполняться только один раз. В языке Си функции MPI_Init передаются указатели на аргументы командной строки программы argc и argv , из которых системой могут извлекаться и передаваться в параллельные процессы некоторые параметры запуска программы.
MPI_FINALIZE(IERR) INTEGER IERR
Завершение параллельной части приложения. Все последующие обращения к любым процедурам MPI , в том числе к MPI_INIT , запрещены. К моменту вызова MPI_FINALIZE каждым процессом программы все действия, требующие его участия в обмене сообщениями , должны быть завершены.
Пример простейшей MPI -программы на языке Фортран выглядит следующим образом:
program example1 include ‘mpif.h’ integer ierr print *, ‘Before MPI_INIT’ call MPI_INIT(ierr) print *, ‘Parallel section’ call MPI_FINALIZE(ierr) print *, ‘After MPI_FINALIZE’ end
В зависимости от реализации MPI строчки ‘Before MPI_INIT’ И ‘After MPI_FINALIZE’ может печатать либо один выделенный процесс, либо все запущенные процессы приложения. Строчку ‘Parallel section’ должны напечатать все процессы. Порядок вывода строк с разных процессов может быть произвольным.
Общая схема MPI -программы на языке Си выглядит примерно следующим образом:
#include «mpi.h» main(int argc, char **argv) MPI_Init(argv); MPI_Finalize() ;
Другие параллельные программы на языке Си с использованием технологии MPI можно найти, например, в Вычислительном полигоне: http://polygon.parallel.ru.
MPI_INITIALIZED(FLAG, IERR) LOGICAL FLAG INTEGER IERR
Процедура возвращает в аргументе FLAG значение .TRUE ., если вызвана из параллельной части приложения, и значение .FALSE . — в противном случае. Это единственная процедура MPI , которую можно вызвать до вызова MPI_INIT .
MPI_COMM_SIZE( COMM , SIZE, IERR) INTEGER COMM , SIZE, IERR
В аргументе SIZE процедура возвращает число параллельных процессов в коммуникаторе сомм .
MPI_COMM_RANK( COMM , RANK , IERR) INTEGER COMM , RANK , IERR
В аргументе RANK процедура возвращает номер процесса в коммуникаторе сомм . Если процедура MPI_COMM_SIZE ДЛЯ ТОГО же коммуникатора сомм вернула значение SIZE , то значение , возвращаемое процедурой MPI_COMM_RANK через переменную RANK , лежит в диапазоне от о до SIZE-1 .
В следующем примере каждый запущенный процесс печатает свой уникальный номер в коммуникаторе MPI_COMM_WORLD И ЧИСЛО процессов в данном коммуникаторе .
program example2 include ‘mpif.h’ integer ierr, size, rank call MPI_INIT(ierr) call MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr) call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr) print *, ‘process ‘, rank, ‘, size ‘, size call MPI_FINALIZE(ierr) end
Строка, соответствующая вызову процедуры print , будет выведена столько раз, сколько процессов было порождено при запуске программы. Порядок появления строк заранее не определен и может быть, вообще говоря, любым. Гарантируется только то, что содержимое отдельных строк не будет перемешано друг с другом.
DOUBLE PRECISION MPI_WTIME(IERR) INTEGER IERR
Эта функция возвращает на вызвавшем процессе астрономическое время в секундах ( вещественное число двойной точности), прошедшее с некоторого момента в прошлом. Если некоторый участок программы окружить вызовами данной функции, то разность возвращаемых значений покажет время работы данного участка. Гарантируется, что момент времени, используемый в качестве точки отсчета, не будет изменен за время существования процесса. Заметим, что эта функция возвращает результат своей работы не через параметры, а явным образом. Таймеры разных процессоров могут быть не синхронизированы и выдавать различные значения, это можно определить по значению параметра MPI_WTIME_IS_GLOBAL (1 — синхронизированы, 0 — нет).
DOUBLE PRECISION MPI_WTICK(IERR) INTEGER IERR
Функция возвращает разрешение таймера на вызвавшем процессе в секундах. Эта функция также возвращает результат своей работы не через параметры, а явным образом.
MPI_GET_PROCESSOR_NAME(NAME, LEN , IERR) CHARACTER*(*) NAME INTEGER LEN , IERR
Процедура возвращает в строке NAME имя узла , на котором запущен вызвавший процесс. В переменной LEN возвращается количество символов в имени, не превышающее значения константы MPI_MAX_PROCESSOR_NAME . С помощью этой процедуры можно определить, на какие именно физические процессоры были спланированы процессы МРI-приложения.
В следующей программе на каждом процессе определяются две характеристики системного таймера : его разрешение и время, требуемое на замер времени (для усреднения получаемого значения выполняется NTIMES замеров). Также в данном примере показано использование процедуры
MPI GET PROCESSOR NAME. program example3 include ‘mpif.h’ integer ierr, rank, len, i, NTIMES parameter (NTIMES = 100) character*(MPI_MAX_PROCESSOR_NAME) name double precision time_start, time_finish, tick call MPI_INIT(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr) call MPI_GET_PROCESSOR_NAME(name, len, ierr) tick = MPI_WTICK(ierr) time_start = MPI_WTIME(ierr) do i = 1, NTIMES time_finish = MPI_WTIME(ierr) end do call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr) print *, ‘processor ‘, name(1: len), ‘, time = ‘, (time_finish-time_start)/NTIMES call MPI_FINALIZE(ierr) end
Задания
- Откомпилировать и проверить эффективность выполнения программы вычисления числа Пи на различном числе процессоров (программа обычно входит в качестве тестового примера в комплект поставки MPI и может находиться, например, в файлах /usr/local/examples/ mpi /fpi.f или cpi.c ).
- Можно ли в процессе работы MPI -программы порождать новые процессы, если в какой-то момент появились свободные процессоры?
- Может ли MPI -программа продолжать работу после аварийного завершения одного из процессов?
- Определить, сколько процессов выполняют текст программы до вызова процедуры MPI_INIT и после вызова процедуры MPI_FINALIZE .
- Определить, синхронизованы ли таймеры разных процессов конкретной системы.
Источник: intuit.ru
Обмен данными с использованием MPI. Работа с библиотекой MPI на примере Intel® MPI Library
В этом посте мы расскажем об организации обмена данными с помощью MPI на примере библиотеки Intel MPI Library. Думаем, что эта информация будет интересна любому, кто хочет познакомиться с областью параллельных высокопроизводительных вычислений на практике.