Mpi описание. Обработчики ошибок, связанные с коммуникаторами. Операции передачи данных

Функции MPI

Производный тип, Операции, Типы данных

Bsend Buffer_attach Get_count ANY_SOURCE Sendrecv_replace ANY_TAG Probe

Allgetherv Alltoall Alltoallv Reduce Rduce_scatter Scan

Производный тип строится из предопределенных типов MPI и ранее определенных производных типов с помощью специальных функций-конструкторов

MPI_Type_contiguous, MPI_Type_vector, MPI_Type_hvector, MPI_Type_indexed, MPI_Type_hindexed, MPI_Type_struct.

Новый производный тип регистрируется вызовом функции MPI_Type_commit. Только после регистрации новый производный тип можно использовать в коммуникационных подпрограммах и при конструировании других типов. Предопределенные типы MPI считаются зарегистрированными.

Когда производный тип становится ненужным, он уничтожается функцией MPI_Type_free.

1) MPI_Init - функция инициализации. В результате выполнения этой функции создается группа процессов, в которую помещаются все процессы приложения, и создается область связи, описываемая предопределенным коммуникатором MPI_COMM_WORLD.

MPI_Type_commit - регистрация типа, MPI_Type_free – уничтожение типа

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

2) MPI_Finalize - Функция завершения MPI программ. Функция закрывает все MPI-процессы и ликвидирует все области связи.

int MPI_Finalize(void);

3) Функция определения числа процессов в области связи MPI_Comm_size . Функция возвращает количество процессов в области связи коммуникатора comm.

int MPI_Comm_size(MPI_Comm comm, int *size);

4) Функция определения номера процесса MPI_Comm_rank . Функция возвращает номер процесса, вызвавшего эту функцию. Номера процессов лежат в диапазоне 0..size-1.

int MPI_Comm_rank(MPI_Comm comm, int *rank);

5) Функция передачи сообщения MPI_Send . Функция выполняет посылку count элементов типа datatype сообщения с идентификатором tag процессу dest в области связи коммуникатора comm.

int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);

6) Функция приема сообщения MPI_Recv . Функция выполняет прием count элементов типа datatype сообщения с идентификатором tag от процесса source в области связи коммуникатора comm.

int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)

7) Функция отсчета времени (таймер)MPI_Wtime . Функция возвращает астрономическое время в секундах, прошедшее с некоторого момента в прошлом (точки отсчета).

double MPI_Wtime(void)

Функции передачи сообщений между процессами делятся на:

Префикс S (synchronous)

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

Префикс B (buffered)

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

Префикс R (ready)

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

Префикс I (immediate)

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

Структура MPI_Status

После чтения сообщения некоторые параметры могут оказаться неизвестными, а именно: число считанных элементов, идентификатор сообщения и адрес отправителя. Эту информацию можно получить с помощью параметра status. Переменные status должны быть явно объявлены в MPI-программе. В языке C status - это структура типа MPI_Status с тремя полями MPI_SOURCE, MPI_TAG, MPI_ERROR.

8) Для определения числа фактически полученных элементов сообщения необходимо использовать специальную функцию MPI_Get_count .

int MPI_Get_count (MPI_Status *status, MPI_Datatype datatype, int *count);

9) Определить параметры полученного сообщения без его чтения можно с помощью функцииMPI_Probe . int MPI_Probe (int source, int tag, MPI_Comm comm, MPI_Status *status);

10) В ситуациях, когда требуется выполнить взаимный обмен данными между процессами, безопаснее использовать совмещенную операцию MPI_Sendrecv . В данной операции посылаемые данные из массива buf замещаются принимаемыми данными.

int MPI_Sendrecv(void *sendbuf, int sendcount, MPI_Datatype sendtype, int dest, int sendtag, void *recvbuf, int recvcount, MPI_Datatype recvtype, int source, MPI_Datatypeа recvtag, MPI_Comm comm, MPI_Status *status);

11) Функция проверки завершения неблокирующей операцииMPI_Test .

int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status);

Это локальная неблокирующая операция. Если связанная с запросом request операция завершена, возвращается flag = true, а status содержит информацию о завершенной операции. Если проверяемая операция не завершена, возвращается flag = false, а значение status в этом случае не определено.

12) Функция снятия запроса без ожидания завершения неблокирующей операцииMPI_Request_free .

int MPI_Request_free(MPI_Request *request);

Параметр request устанавливается в значение MPI_REQUEST_NULL.

13) Достижение эффективного выполнения операции передачи данных от одного процесса всем процессам программы(широковещательная рассылка данных) может быть обеспечено при помощи функции MPI:

int MPI_Bcast(void *buf,int count,MPI_Datatype type,int root,MPI_Comm comm)

Функция MPI_Bcast осуществляет рассылку данных из буфера buf, содержащего count элементов типа type с процесса, имеющего номер root, всем процессам, входящим в коммуникатор comm.

14) При необходимости приема сообщения от любого процесса-отправителя для параметра source может быть указано значениеMPI_ANY_SOURCE

15) При необходимости приема сообщения с любым тегом для параметра tag может быть указано значение MPI_ANY_TAG

16) Параметр status позволяет определить ряд характеристик принятого сообщения:

- status.MPI_SOURCE – ранг процесса-отправителя принятого сообщения,

- status.MPI_TAG - тег принятого сообщения.

17) Функция

MPI_Get_coun t(MPI_Status *status, MPI_Datatype type, int *count)

возвращает в переменной count количество элементов типа type в принятом сообщении.

18) Операции передачи данных от всех процессов одному процессу. В этой операции над собираемыми

значениями осуществляется та или иная обработка данных(для подчеркивания последнего момента данная операция еще именуется операцией редукции данных)

int MPI_Reduce (void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,int root,MPI_Comm comm)

19) Синхронизация процессов, т.е. одновременное достижение процессами тех или иных точек процесса вычислений, обеспечивается при помощи функцииMPI: int MPI_Barrier (MPI_Comm comm); Функция MPI_Barrier определяет коллективную операции и, тем самым, при использовании должна вызываться всеми процессами используемого коммун+икатора. При вызове функции MPI_Barrier

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

20) Для использования буферизованного режима передачи должен быть создан и переданMPI буфер памяти

для буферизации сообщений– используемая для этого функция имеет вид: int MPI_Buffer_attach (void *buf, int size),

- bufбуфер памяти для буферизации сообщений,

- size– размер буфера.

21) После завершения работы с буфером он должен быть отключен отMPI при помощи функции:

int MPI_Buffer_detach (void *buf, int *size).

22) Достижение эффективного и гарантированного одновременного выполнения операций передачи и приема данных может быть обеспечено при помощи функцииMPI:

int MPI_Sendrecv (void *sbuf,int scount,MPI_Datatype stype,int dest, int stag, void *rbuf,int rcount,MPI_Datatype

rtype,int source,int rtag, MPI_Comm comm, MPI_Status *status)

23) Когда сообщения имеют одинаковый тип, в MPI имеется возможность использования единого буфера: intMPI_Sendrecv_replace (void *buf, int count, MPI_Datatype type, int dest,

int stag, int source, int rtag, MPI_Comm comm, MPI_Status* status)

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

int MPI_Scatter (void *sbuf,int scount,MPI_Datatype stype,

25) Операция обобщенной передачи данных от всех процессоров одному процессу(сбор данных) является обратной к процедуре распределения данных(см. рис. 4.5). Для выполнения этой операции вMPI предназначена функция:

int MPI_Gather (void *sbuf,int scount,MPI_Datatype stype,

void *rbuf,int rcount,MPI_Datatype rtype, int root, MPI_Comm comm)

26) Следует отметить, что при использовании функции MPI_Gather сборка данных осуществляется только

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

необходимо использовать функцию сбора и рассылки:

int MPI_Allgather (void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype, MPI_Comm comm)

27) Передача данных от всех процессов всем процессам является наиболее общей операцией передачи данных(см. рис. 4.6). Выполнение данной операции может быть обеспечено при помощи функции:

int MPI_Alltoall (void *sbuf,int scount,MPI_Datatype stype, void *rbuf,int rcount,MPI_Datatype rtype,MPI_Comm comm)

28) Функция MPI_Reduce обеспечивает получение результатов редукции данных

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

int MPI_Allreduce (void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,MPI_Comm comm).

29) И еще один вариант операции сбора и обработки данных, при котором обеспечивается получение и всех частичных результатов редуцирования, может быть получен при помощи функции:

int MPI_Scan (void *sendbuf, void *recvbuf,int count,MPI_Datatype type, MPI_Op op,MPI_Comm comm).

Общая схема выполнения функции MPI_Scan показана на рис. 4.7. Элементы получаемых сообщений представляют собой результаты обработки соответствующих элементов передаваемых процессами сообщений, при этом для получения результатов на процессе с рангом i, 0≤i

30) Начальное значение переменной bufpos должно быть сформировано до начала упаковки и далее устанавливается функцией MPI_Pack . Вызов функции MPI_Pack осуществляется последовательно для упаковки всех необходимых данных.

int MPI_Pack_size (int count, MPI_Datatype type, MPI_Comm comm, int *size)

31) После упаковки всех необходимых данных подготовленный буфер может быть использован в функциях передачи данных с указанием типа MPI_PACKED.

После получения сообщения с типом MPI_PACKED данные могут быть распакованы при помощи функции:

int MPI_Unpack (void *buf, int bufsize, int *bufpos, void *data, int count, MPI_Datatype type, MPI_Comm comm)

Complex Instruction Set Computer

CISC (англ. Complex instruction set computing, или англ. complex instruction set computer -

компьютер с полным набором команд) - концепция проектирования процессоров, которая характеризуется следующим набором свойств:

сравнительно небольшое число регистров общего назначения;

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

· большое количество методов адресации;

· большое количество форматов команд различной разрядности;

· преобладание двухадресного формата команд;

· наличие команд обработки типа регистр-память.

Недостатки :

высокая стоимость аппаратной части; сложности с распараллеливанием вычислений.

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

Reduced Instruction Set Computer

В ее основе лежат принципы RISC-архитектуры: фиксированный формат команд, регистровые операции, однотактовое выполнение команд, простые способы адресации, большой регистровый файл. В то же время имеется несколько существенных особенностей, отличающих данную архитектуру от архитектур других RISC-процессоров. К ним относятся: независимый набор регистров для каждого из исполнительных устройств; включение в систему отдельных CISC-подобных инструкций; отсутствие механизма “задержанных переходов”; оригинальный способ реализации условных переходов. Основной областью применения микропроцессоров с архитектурой являются высокопроизводительные серверы и суперкомпьютеры.

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

и фиксированного формата.

В чем смысл технологии буфера целевых адресов переходов

В процессоре предусмотрен механизм динамического прогнозирования направления переходов. С этой

целью на кристалле размещена небольшая кэш-память, которая называется буфером целевых адресов переходов (BTB), и две независимые пары буферов предварительной выборки команд (по два 32битовых буфера на каждый конвейер). Буфер целевых адресов переходов хранит адреса команд, которые находятся в буферах предварительной выборки. Работа буферов предварительной выборки организована таким образом, что в каждый момент времени осуществляется выборка команд только в один из буферов соответствующей пары. При обнаружении в потоке команд операции перехода вычисленный адрес перехода сравнивается с адресами, хранящимися в буфере BTB. В случае совпадения предсказывается, что переход будет выполнен, и разрешается работа другого буфера предварительной выборки, который начинает выдавать команды для выполнения в соответствующий конвейер. При несовпадении считается, что переход выполняться не будет и буфер предварительной выборки не переключается, продолжая обычный порядок выдачи команд. Это позволяет избежать простоев конвейеров

Структурные конфликты и способы их минимизации

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

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

Минимизация: конвейер приостанавливает выполнение одной из команд до тех пор, пока не станет доступным требуемое устройство.

Конфликты по данным, остановы конвейера и реализация механизма обходов

Одним из факторов, который оказывает существенное влияние на производительность конвейерных систем, являются межкомандные логические зависимости. Конфликты по данным возникают в том случае, когда применение конвейерной обработки может изменить порядок обращений за операндами так, что этот порядок будет отличаться от порядка, который наблюдается при последовательном выполнении команд на неконвейерной машине. Проблема, поставленная в этом примере, может быть разрешена с помощью достаточно простой аппаратной техники, которая называется пересылкой или продвижением данных (data forwarding), обходом (data bypassing), иногда закороткой (short-circuiting).

Конфликты по данным, приводящие к приостановке конвейера

Вместо этого нам нужна дополнительная аппаратура, называемая аппаратурой внутренних блокировок конвейера (pipeline interlook), чтобы обеспечить корректное выполнение примера. Вообще такого рода аппаратура обнаруживает конфликты и приостанавливает конвейер до тех пор, пока существует конфликт. В этом случае эта аппаратура приостанавливает конвейер начиная с команды, которая хочет использовать данные в то время, когда предыдущая команда, результат которой является операндом для нашей, вырабатывает этот результат. Эта аппаратура вызывает приостановку конвейера или появление "пузыря" точно также, как и в случае структурных конфликтов.

Буфера прогнозирования условных переходов

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

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

Основные принципы и пример
В качестве примера будет использоваться расчет экспоненты (e). Один из вариантов ее нахождения - ряд Тейлора:
e^x=∑((x^n)/n!) , где суммирование происходит от n=0 до бесконечности.

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

Количество слагаемых, которое будет рассчитываться в каждом отдельно взятом процессоре, зависит как и от длины интервала n, так и от имеющегося количества процессоров k, которые смогут участвовать в процессе вычисления. Так, например, если длина интервала n=4, а в вычислениях участвуют пять процессоров (k=5), то с первого по четвертый процессоры получат по одному слагаемому, а пятый будет не задействован. В случае же если n=10, а k=5, каждому процессору достанется по два слагаемых для вычисления.

Изначально, первый процессор с помощью функции широковещательной рассылки MPI_Bcast отправляет остальным значение заданной пользователями переменной n. В общем случае функция MPI_Bcast имеет следующий формат:
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm), где buffer – это адрес буфера с элементом, сount – количество элементов, datatype – соответствующий тип данных в MPI, root – ранг главного процессора, который занимается пересылкой, а comm- имя коммуникатора.
В моем случае в роли главного процессора, как уже говорилось, будет выступать первый процессор с рангом 0.

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

В ходе выполнения цикла слагаемые будут прибавляться в отдельную переменную и, после его завершения, полученная сумма отправится в главный процессор. Для этого будет использоваться функция операции приведения MPI_Reduce. В общем виде она выглядит следующим образом:
int MPI_Reduce(void *buf, void *result, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm)

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

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

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

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

Алгоритм выполнения кода
1. Из визуальной оболочки в программу передается значение числа n, которое затем с помощью функции широковещательной рассылки отправляется по всем процессорам.
2. При инициализации первого главного процессора, запускается таймер.
3. Каждый процессор выполняет цикл, где значением приращения является количество процессоров в системе. В каждой итерации цикла вычисляется слагаемое и сумма таких слагаемых сохраняется в переменную drobSum.
4. После завершения цикла каждый процессор суммирует свое значение drobSum к переменной Result, используя для этого функцию приведения MPI_Reduce.
5. После завершения расчетов на всех процессорах, первый главный процессор останавливает таймер и отправляет в поток вывода получившееся значение переменной Result.
6. В поток вывода отправляется также и отмеренное нашим таймером значение времени в милисекундах.
Листинг кода
Программа написана на С++, будем считать что аргументы для выполнения передаются из внешней оболочки. Код выглядит следующим образом:
#include "mpi.h"
#include
#include
using namespace std;

double Fact(int n)
{
if (n==0)
return 1;
else
return n*Fact(n-1);
}

int main(int argc, char *argv)
{
SetConsoleOutputCP(1251);
int n;
int myid;
int numprocs;
int i;
int rc;
long double drob,drobSum=0,Result, sum;
double startwtime = 0.0;
double endwtime;

N = atoi(argv);

if (rc= MPI_Init(&argc, &argv))
{
cout << "Ошибка запуска, выполнение остановлено " << endl;
MPI_Abort(MPI_COMM_WORLD, rc);
}

MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);

if (myid == 0)
{

Startwtime = MPI_Wtime();
}
MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);

for (i = myid; i <= n; i += numprocs)
{
drob = 1/Fact(i);
drobSum += drob;
}

MPI_Reduce(&drobSum, &Result, 1, MPI_LONG_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
cout.precision(20);
if (myid == 0)
{
cout << Result << endl;
endwtime = MPI_Wtime();
cout << (endwtime-startwtime)*1000 << endl;
}

MPI_Finalize();
return 0;
}


* This source code was highlighted with Source Code Highlighter .
Вывод
Таким образом мы получили простенькую программу для подсчета экспоненты с использованием сразу нескольких процессоров. Наверное, узким местом является хранением самого результата, потому что с увеличением количества разрядов вмещать значение с использованием стандартных типов банально не выйдет и это место требует проработки. Пожалуй, достаточно рациональным решением является запись результата в файл, хотя, в виду чисто учебной функции этого примера, особо на этом внимание можно не акцентировать.

В этой заметке показано как установить MPI, подключить его к Visual Studio, а затем использовать с заданными параметрами (числом вычислительных узлов). В статье используется Visual Studio 2015, т.к. именно с ней возникали проблемы у моих студентов (эта заметка написана студентами для студентов), однако вероятно инструкция подойдет и для других версий.

Шаг 1:
Необходимо установить пакет HPC Pack 2008 SDK SP2 (в вашем случае может быть уже другая версия), доступный на официальном сайте Microsoft. Разрядность пакета и системы должны соответствовать.

Шаг 2:
Необходимо настроить пути, для этого переходим во вкладку Debug — Properties:

“C:\Program Files\Microsoft HPC Pack 2008 SDK\Include”

В поле Library Directories:

“C:\Program Files\Microsoft HPC Pack 2008 SDK\Lib\amd64”

В поле с библиотеками, если стоит 32 разрядная версия, вместо amd64 нужно прописать i386.

Msmpi.lib

:

Шаг 3 :

Для настройки запуска необходимо перейти во вкладку Debugging и в поле Command указать:

“C:\Program Files\Microsoft HPC Pack 2008 SDK\Bin\mpiexec.exe”

В поле Command Arguments указать, например,

N 4 $(TargetPath)

Число 4 указывает на количество процессов.

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

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

Для этого после установки достаточно прописать в поле Command вкладки Debugging путь:

“C:\Program Files\Microsoft MPI\Bin\mpiexec.exe”

Также перед запуском программы не забудьте указать её разрядность:

Пример запуска программы с MPI :

#include #include using namespace std; int main(int argc, char **argv) { int rank, size; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); cout << "The number of processes: " << size << " my number is " << rank << endl; MPI_Finalize(); return 0; }

Работа программы на 2 узлах:

Распараллеливание на языке Си
Пример 3b . Распараллеливание на языке Фортран
Пример 4a . Определение характеристик системного таймера на языке Си
Пример 4b . Определение характеристик системного таймера на языке Фортран

1.4. Передача и прием сообщений между отдельными процессами

1.4.1. Операции типа точка-точка

1.4.2. Передача и прием сообщений с блокировкой

Пример 5a . Обмен сообщениями двух процессов на языке Си
Пример 5b . Обмен сообщениями двух процессов на языке Фортран
Пример 6a . Обмен сообщениями четных и нечетных процессов на языке Си
Пример 6b . Обмен сообщениями четных и нечетных процессов на языке Фортран
Пример 7a . Пересылка несуществующему процессу на языке Си
Пример 7b . Пересылка несуществующему процессу на языке Фортран
Пример 8a . Буферизованная посылка данных на языке Си
Пример 8b . Буферизованная посылка данных на языке Фортран
Пример 9a . Получение информации об атрибутах сообщения на языке Си
Пример 9b . Получение информации об атрибутах сообщения на языке Фортран
Пример 10a . Определение латентности и пропускной способности на языке Си
Пример 10b . Определение латентности и пропускной способности на языке Фортран

1.4.3. Передача и прием сообщений без блокировки

Пример 11a . Обмен по кольцевой топологии при помощи неблокирующих операций на языке Си
Пример 11b . Обмен по кольцевой топологии при помощи неблокирующих операций на языке Фортран
Пример 12a . Коммуникационная схема «мастер - рабочие» на языке Си
Пример 12b . Коммуникационная схема «мастер - рабочие» на языке Фортран
Пример 13a . Транспонирование матрицы на языке Си
Пример 13b . Транспонирование матрицы на языке Фортран

1.4.4. Отложенные запросы на взаимодействие

Пример 14a . Схема итерационного метода с обменом по кольцевой топологии при помощи отложенных запросов на языке Си
Пример 14b . Схема итерационного метода с обменом по кольцевой топологии при помощи отложенных запросов на языке Фортран

1.4.5. Тупиковые ситуации (deadlock)

Пример 15a . Обмен по кольцевой топологии при помощи процедуры MPI_Sendrecv на языке Си
Пример 15b . Обмен по кольцевой топологии при помощи процедуры MPI_SENDRECV на языке Фортран

1.5. Коллективные взаимодействия процессов

1.5.1. Общие положения

1.5.2. Барьер

Пример 16a . Моделирование барьерной синхронизации на языке Си
Пример 16b . Моделирование барьерной синхронизации на языке Фортран

1.5.3. Коллективные операции пересылки данных

1.5.4. Глобальные операции

Пример 17a . Моделирование глобального суммирования при помощи схемы сдваивания и коллективной операции MPI_Reduce на языке Си
Пример 17b . Моделирование глобального суммирования при помощи схемы сдваивания и коллективной операции MPI_Reduce на языке Фортран

1.5.5. Пользовательские глобальные операции

Пример 18a . Пользовательская глобальная функция на языке Си
Пример 18b . Пользовательская глобальная функция на языке Фортран

1.6. Группы и коммуникаторы

1.6.1. Общие положения

1.6.2. Операции с группами процессов

Пример 19a . Работа с группами на языке Си
Пример 19b . Работа с группами на языке Фортран

1.6.3. Операции с коммуникаторами

Пример 20a . Разбиение коммуникатора на языке Си
Пример 20b . Разбиение коммуникатора на языке Фортран
Пример 21a . Перенумерация процессов на языке Си
Пример 21b . Перенумерация процессов на языке Фортран

1.6.4. Интеркоммуникаторы

Пример 22a . Cхема «мастер - рабочие» с использованием интеркоммуникатора на языке Си
Пример 22b . Схема «мастер - рабочие» с использованием интеркоммуникатора на языке Фортран

1.6.5. Атрибуты

1.7. Виртуальные топологии

1.7.1. Общие положения

1.7.2. Декартова топология

1.7.3. Топология графа

Пример 23a . Cхема «мастер - рабочие» с использованием графовой топологии на языке Си
Пример 23b . Схема «мастер - рабочие» с использованием графовой топологии на языке Фортран

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

1.8.1. Общие положения

1.8.2. Производные типы данных

Пример 24a . Перестановка столбцов матрицы в обратном порядке на языке Си
Пример 24b . Перестановка столбцов матрицы в обратном порядке на языке Фортран

1.8.3. Упаковка данных

Пример 25a . Пересылка упакованных данных на языке Си
Пример 25b . Пересылка упакованных данных на языке Фортран

1.9. Объект info

1.9.1. Общие положения

1.9.2. Работа с объектом info

1.10. Динамическое управление процессами

1.10.1. Общие положения

1.10.2.Порождение процессов

master.c
slave.c
Пример 26a. Схема «мастер - рабочие» с использованием порождения процессов на языке Си
master.f
slave.f
Пример 26b. Схема «мастер - рабочие» с использованием порождения процессов на языке Фортран

1.10.3. Клиент-серверная связь

server.c
client.c
Пример 27a. Обмен данными между сервером и клиентом посредством публичного имени на языке Си
server.f
client.f
Пример 27b. Обмен данными между сервером и клиентом посредством публичного имени на языке Фортран

1.10.4. Удаление связи процессов

1.10.5. Связь через сокеты

1.11. Односторонние коммуникации

1.11.1. Общие положения

1.11.2. Работа с окном

1.11.3. Передача данных

1.11.4. Синхронизация

Пример 28a
Пример 28b
Пример 29a . Обмен по кольцевой топологии при помощи односторонних коммуникаций на языке Си
Пример 29b . Обмен по кольцевой топологии при помощи односторонних коммуникаций на языке Фортран
Пример 30a . Обмен по кольцевой топологии при помощи односторонних коммуникаций на языке Си
Пример 30b . Обмен по кольцевой топологии при помощи односторонних коммуникаций на языке Фортран

1.12. Внешние интерфейсы

1.12.1. Обобщенные запросы

1.12.2. Информация из статуса

1.12.3. Нити

1.13. Параллельный ввод/вывод

1.13.1. Определения

1.13.2. Работа с файлами

1.13.3. Доступ к данным

Пример 31a . Буферизованное чтение из файла на языке Си
Пример 31b . Буферизованное чтение из файла на языке Фортран
Пример 32a . Коллективное чтение из файла на языке Си
Пример 32b . Коллективное чтение из файла на языке Фортран

1.14. Обработка ошибок

1.14.1. Общие положения

1.14.2. Обработчики ошибок, связанные с коммуникаторами

1.14.3. Обработчики ошибок, связанные с окнами

1.14.4. Обработчики ошибок, связанные с файлами

1.14.5. Дополнительные процедуры

1.14.6. Коды и классы ошибок

1.14.7. Вызов обработчиков ошибок

Пример 33a . Обработка ошибок на языке Си
Пример 33b . Обработка ошибок на языке Фортран

Глава 2 Технология параллельного программирования OpenMP

2.1. Введение

2.2. Основные понятия

2.2.1. Компиляция программы

Пример 34a . Условная компиляция на языке Си
Пример 34b
Пример 34c . Условная компиляция на языке Фортран

2.2.2. Модель параллельной программы

2.2.3. Директивы и процедуры

2.2.4. Выполнение программы

2.2.5. Замер времени

Пример 35a . Работа с системными таймерами на языке Си
Пример 35b . Работа с системными таймерами на языке Фортран

2.3. Параллельные и последовательные области

2.3.1. Директива parallel

Пример 36a . Параллельная область на языке Си
Пример 36b . Параллельная область на языке Фортран
Пример 37a . Опция reduction на языке Си
Пример 37b . Опция reduction на языке Фортран

2.3.2. Сокращенная запись

2.3.3. Переменные среды и вспомогательные процедуры

Пример 38a . Процедура omp_set_num_threads и опция num_threads на языке Си
Пример 38b . Процедура omp_set_num_threads и опция num_threads на языке Фортран
Пример 39a . Процедуры omp_set_dynamic и omp_get_dynamic на языке Си
Пример 39b . Процедуры omp_set_dynamic и omp_get_dynamic на языке Фортран
Пример 40a . Вложенные параллельные области на языке Си
Пример 40b . Вложенные параллельные области на языке Фортран
Пример 41a . Функция omp_in_parallel на языке Си
Пример 41b . Функция omp_in_parallel на языке Фортран

2.3.4. Директива single

Пример 42a . Директива single и опция nowait на языке Си
Пример 42b . Директива single и опция nowait на языке Фортран
Пример 43a . Опция copyprivate на языке Си
Пример 43b . Опция copyprivate на языке Фортран

2.3.5. Директива master

Пример 44a . Директива master на языке Си
Пример 44b . Директива master на языке Фортран

2.4. Модель данных

Пример 45a . Опция private на языке Си
Пример 45b . Опция private на языке Фортран
Пример 46a . Опция shared на языке Си
Пример 46b . Опция shared на языке Фортран
Пример 47a . Опция firstprivate на языке Си
Пример 47b . Опция firstprivate на языке Фортран
Пример 48a . Директива threadprivate на языке Си
Пример 48b . Директива threadprivate на языке Фортран
Пример 49a . Опция copyin на языке Си
Пример 49b . Опция copyin на языке Фортран

2.5. Распределение работы

2.5.1. Низкоуровневое распараллеливание

Пример 50a . Процедуры omp_get_num_threads и omp_get_thread_num на языке Си
Пример 50b . Процедуры omp_get_num_threads и omp_get_thread_num на языке Фортран

2.5.2. Параллельные циклы

Пример 51a . Директива for на языке Си
Пример 51b . Директива do на языке Фортран
Пример 52a . Опция schedule на языке Си
Пример 52b . Опция schedule на языке Фортран
Пример 53a . Опция schedule на языке Си

Аннотация: Лекция посвящена рассмотрению технологии MPI как стандарта параллельного программирования для систем с распределенной памятью. Рассматриваются основные режимы передачи данных. Вводятся такие понятия, как группы процессов и коммуникаторы. Рассматриваются основные типы данных, операции "точка-точка", коллективные операции, операции синхронизации и измерения времени.

Цель лекции: Лекция направлена на изучение общей методики разработки параллельных алгоритмов.

Видеозапись лекции - (объем - 134 МБ).

5.1. MPI: основные понятия и определения

Рассмотрим ряд понятий и определений, являющихся основополагающими для стандарта MPI .

5.1.1. Понятие параллельной программы

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

Каждый процесс параллельной программы порождается на основе копии одного и того же программного кода (модель SPMP ). Данный программный код, представленный в виде исполняемой программы, должен быть доступен в момент запуска параллельной программы на всех используемых процессорах. Исходный программный код для исполняемой программы разрабатывается на алгоритмических языках C или Fortran с использованием той или иной реализации библиотеки MPI.

Количество процессов и число используемых процессоров определяется в момент запуска параллельной программы средствами среды исполнения MPI-программ и в ходе вычислений меняться не может (в стандарте MPI-2 предусматривается возможность динамического изменения количества процессов). Все процессы программы последовательно перенумерованы от 0 до p-1 , где p есть общее количество процессов. Номер процесса именуется рангом процесса.

5.1.2. Операции передачи данных

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

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

Как уже отмечалось ранее, стандарт MPI предусматривает необходимость реализации большинства основных коллективных операций передачи данных – см. подразделы 5.2 и 5.4.

5.1.3. Понятие коммуникаторов

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

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

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

При необходимости передачи данных между процессами из разных групп необходимо создавать глобальный коммуникатор (intercommunicator ).

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

5.1.4. Типы данных

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

Подробное рассмотрение возможностей MPI для работы с производными типами данных будет выполнено в подразделе 5.5.

5.1.5. Виртуальные топологии

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

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

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

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

И, наконец, последний ряд замечаний перед началом рассмотрения MPI:

  • Описание функций и все приводимые примеры программ будут представлены на алгоритмическом языке C; особенности использования MPI для алгоритмического языка Fortran будут даны в п. 5.8.1,
  • Краткая характеристика имеющихся реализаций библиотек MPI и общее описание среды выполнения MPI программ будут рассмотрены в п. 5.8.2,
  • Основное изложение возможностей MPI будет ориентировано на стандарт версии 1.2 (MPI-1 ); дополнительные свойства стандарта версии 2.0 буду представлены в п. 5.8.3.

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

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

5.2.1. Основы MPI

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

5.2.1.1 Инициализация и завершение MPI программ

Первой вызываемой функцией MPI должна быть функция:

int MPI_Init (int *agrc, char ***argv);

для инициализации среды выполнения MPI-программы. Параметрами функции являются количество аргументов в командной строке и текст самой командной строки.

Последней вызываемой функцией MPI обязательно должна являться функция:

int MPI_Finalize (void);

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

#include "mpi.h" int main (int argc, char *argv) { <программный код без использования MPI функций> MPI_Init (&agrc, &argv); <программный код с использованием MPI функций> MPI_Finalize(); <программный код без использования MPI функций> return 0; }

Следует отметить:

  1. Файл mpi.h содержит определения именованных констант, прототипов функций и типов данных библиотеки MPI,
  2. Функции MPI_Init и MPI_Finalize являются обязательными и должны быть выполнены (и только один раз) каждым процессом параллельной программы,
  3. Перед вызовом MPI_Init может быть использована функция MPI_Initialized для определения того, был ли ранее выполнен вызов MPI_Init .

Рассмотренные примеры функций дают представление синтаксиса именования функций в MPI. Имени функции предшествует префикс MPI, далее следует одно или несколько слов названия, первое слово в имени функции начинается с заглавного символа, слова разделяются знаком подчеркивания. Названия функций MPI, как правило, поясняют назначение выполняемых функцией действий.

Следует отметить:

  • Коммуникатор MPI_COMM_WORLD , как отмечалось ранее, создается по умолчанию и представляет все процессы выполняемой параллельной программы,
  • Ранг, получаемый при помощи функции MPI_Comm_rank , является рангом процесса, выполнившего вызов этой функции, т.е. переменная ProcRank будет принимать различные значения в разных процессах.


Понравилась статья? Поделиться с друзьями: