Основы параллельного программирования с использованием MPI Лекция 8 Немнюгин Сергей Андреевич Санкт-Петербургский государственный университет физический факультет кафедра вычислительной физики Лекция 8 Аннотация В заключительной лекции даётся краткий обзор тех возможностей MPI, которые не рассматривались в предыдущих лекциях. Среди них атрибуты коммуникатора, односторонние обмены, дополнительные вопросы организации коллективных обменов, параллельные операции ввода-вывода, внешние интерфейсы, а также средства управления процессами из MPI-программы в процессе её выполнения. Рассматриваются программные инструменты отладки и оптимизации параллельных программ. 2008 План лекции Атрибуты коммуникатора. MPI-1 и MPI-2, сравнительный обзор. Запуск процессов. Демон mpd. Односторонние обмены. Взаимодействие между группами процессов. Коллективные обмены. Параллельные операции ввода-вывода. Программные инструменты отладки и оптимизации MPI-программ. 2008 Атрибуты 2008 Атрибуты Атрибут коммуникатора – дополнительная информация, добавляемая к коммуникатору с помощью механизма кэширования. Если какая-то информация записана в качестве атрибута в одном из процессов, она автоматически становится доступной всем другим процессам из данного коммуникатора. Специальной организации пересылок сообщений при этом не требуется, система сама позаботится о передаче необходимой информации. Атрибуты определяются только для того коммуникатора, к которому они присоединены. Они могут дублироваться только при дублировании коммуникатора - других способов их передачи от одного коммуникатора другому нет. 2008 Атрибуты В C атрибуты имеют тип void *. Обычно это указатели на структуры, которые содержат необходимую информацию или обработчики для объекта MPI. В программах на языке Fortran атрибуты имеют тип integer. Идентификатор атрибута - целое число, которое назначается системой автоматически и называется ключом атрибута. Атрибуты бывают системные и пользовательские. Системные атрибуты не могут модифицироваться программистом. К их числу относятся топология, адрес обработчика ошибок и некоторые другие. Значения ключей являются глобальными и могут использоваться всеми коммуникаторами программы. 2008 Атрибуты В MPI реализованы (локальные) операции с атрибутами. Подпрограмма: int MPI_Keyval_create(MPI_Copy_function *copy_fn, MPI_Delete_function *delete_fn, int *keyval, void *extra_state) MPI_Keyval_create(copy_fn, delete_fn, keyval, extra_state, ierr) предназначена для создания нового ключа атрибута keyval (выходной параметр). Ключи уникальны для каждого процесса и не видны пользователю, хотя явным образом хранятся в виде целых значений. Будучи однажды задан, ключ может быть использован для задания атрибутов и доступа к ним в любом коммуникаторе. Функция copy_fn вызывается, когда коммуникатор дублируется подпрограммой MPI_Comm_dup, а функция delete_fn используется для удаления. Параметр extra_state задает дополнительную информацию для функций копирования и удаления. 2008 Атрибуты Функция MPI_Copy_function определяется следующим образом: typedef int MPI_Copy_function(MPI_Comm oldcomm, int keyval, void *extra_state, void *attribute_val_in, void *attribute_val_out, int *flag) subroutine copy_function(oldcomm, keyval, extra_state, attribute_val_in, attribute_val_out, flag, ierr) 2008 Атрибуты Функция копирования вызывается для каждого значения ключа в исходном коммуникаторе в произвольном порядке. Каждое обращение к функции копирования выполняется со значением ключа и соответствующим ему атрибутом. Если она возвращает значение флага flag = 0, атрибут удаляется из продублированного коммуникатора. В противном случае (flag = 1) устанавливается новое значение атрибута, равное значению, возвращенному в параметре attribute_val_out. 2008 Атрибуты Удалить ключ атрибута можно с помощью вызова подпрограммы : int MPI_Keyval_free(int *keyval) MPI_Keyval_free(keyval, ierr) Используемый атрибут можно удалить, поскольку фактическое удаление происходит только после того, как будут удалены все ссылки на атрибут. Эти ссылки должны быть явным образом удалены программой, например, посредством вызова MPI_Attr_delete каждый такой вызов удаляет один экземпляр атрибута, либо вызовом MPI_Comm_free, который удаляет все экземпляры атрибута, связанные с удаляемым коммуникатором. 2008 Атрибуты Подпрограмма: int MPI_Attr_put(MPI_Comm comm, int keyval, void* attribute) MPI_Attr_put(comm, keyval, attribute, ierr) используется для задания атрибута attribute, который в дальнейшем может использоваться подпрограммой MPI_Attr_get. С атрибутом ассоциируется значение ключа keyval. Подпрограмма: int MPI_Attr_get(MPI_Comm comm, int keyval, void *attribute, int *flag) MPI_Attr_get(comm, keyval, attribute, flag, ierr) возвращает значение атрибута attribute, соответствующее значению ключа keyval. Первый параметр задает коммуникатор, с которым связан атрибут. 2008 Атрибуты Подпрограмма: int MPI_Attr_delete(MPI_Comm comm, int keyval) MPI_Attr_delete(comm, keyval, ierr) удаляет атрибут с указанным значением ключа. Делается это с помощью функции удаления атрибута delete_fn, заданной при создании keyval. Параметр comm задает коммуникатор, с которым связан атрибут. 2008 Атрибуты Пример program main_mpi include 'mpif.h' integer rank, ierr integer numprocs, newcomm integer key, attr, extra external copy_fun, del_fun logical flag call MPI_Init(ierr) call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr) call MPI_Keyval_create(copy_fun, del_fun, key, extra, ierr) ATTR = 120 call MPI_Attr_put(MPI_COMM_WORLD, key, attr, ierr) call MPI_Attr_get(MPI_COMM_WORLD, key, attr, flag, ierr) print *, "PROCESS = ", RANK, " BEFORE DUP ATTRIBUTE = ", attr call MPI_Comm_dup(MPI_COMM_WORLD, newcomm, ierr) 2008 Атрибуты call MPI_Attr_get(newcomm, key, attr, flag, ierr) if (flag) then print *, "process = ", rank, " after dup attribute = ", attr end if call MPI_Comm_free(newcomm, ierr) call MPI_Finalize(ierr) stop end 2008 Атрибуты subroutine copy_fun(comm, keyval, extra, attr_in, attr_out, flag, ierr) integer comm, keyval, fuzzy, attr_in, attr_out logical flag include 'mpif.h' attr_out = attr_in + 1 flag =.true. ierr = MPI_SUCCESS end subroutine del_fun(comm, keyval, attr, extra, ierr) integer comm, keyval, attr, extra, ierr include 'mpif.h' ierr = MPI_SUCCESS if(keyval /= MPI_KEYVAL_INVALID) then attr = attr - 1 end if return end 2008 Атрибуты Результат выполнения программы: 2008 MPI-1 и MPI-2. Сравнительный обзор 2008 MPI-1 и MPI-2. Сравнительный обзор В спецификации MPI-2 появились новые возможности, превратившие MPI в ещё более гибкий инструмент разработки параллельных программ. Краткий перечень новых (по сравнению с MPI-1) возможностей: возможность запуска новых процессов во время выполнения MPIпрограммы; односторонние двухточечные обмены; параллельные операции ввода-вывода; модифицированные привязки к языкам; новые предопределённые типы данных; расширенные возможности коллективных обменов; внешние интерфейсы; поддержка многопоточности и другие. 2008 Динамический запуск процессов 2008 Динамический запуск процессов Возможность запуска новых процессов во время выполнения MPI-программы В MPI-1 параллельная программа запускается в определённом и фиксированном количестве процессов. Это не позволяет приложению, например, «подстраиваться» под изменяющуюся трудоёмкость расчёта. В то же время такой инструмент параллельного программирования как PVM поддерживает возможность динамического изменения числа процессов параллельного приложения. Эта возможность появилась в MPI-2. Дополнительный процесс (несколько процессов) может быть запущен во время выполнения программы. Процесс может быть также остановлен. 2008 Динамический запуск процессов Запуск одного процесса выполняется с помощью обращения к подпрограмме: int MPI_Comm_spawn(char *command, char *argv[], int maxprocs, MPI_Info info, int root, MPI_Comm comm, MPI_Comm *intercomm, int array_of_errcodes[]) MPI_Comm_spawn(command, argv, maxprocs, info, root, comm, intercomm, array_of_errcodes, ierror) Входные параметры: command – командная строка запуска процесса; argv – аргументы командной строки запуска процесса; maxprocs – максимальное количество запускаемых процессов; info – указывает системе как и где запускается процесс; root – ранг главного процесса. 2008 Динамический запуск процессов Выходные параметры: intercomm – интеркоммуникатор между исходной группой процессов и вновь запущенными процессами; array_of_errcodes – коды завершения для запущенных процессов. При запуске группы процессов для них создаётся собственный коммуникатор MPI_COMM_WORLD, отличный от такого же для родительских процессов. Это коллективная операция. Она завершается после того, как во всех дочерних процессах состоится вызов MPI_Init. Завершение данного вызова в родительском процессе не означает, что в дочерних процессах завершены все вызовы MPI_Init. Интеркоммуникатор intercomm содержит родительский процесс в локальной группе и дочерние процессы. 2008 Динамический запуск процессов Командная строка запуска дочернего процесса представляет собой строку, содержащую имя исполняемого файла. Дочерний процесс обязательно должен вызывать MPI_Init, иначе результат не определён. Аргумент argv является массивом строковых представляющих аргументы запускаемых программ. значений, При вызове MPI_Comm_spawn предприниматеся попытка запустить maxprocs процессов. Если какой-то процесс не может быть запущен, возвращается значение MPI_ERR_SPAWN. Аргумент info создаётся вызовом специальной подпрограммы MPI_Info_create. 2008 Динамический запуск процессов В том случае, когда необходимо запустить несколько разных исполняемых файлов или один файл, но с разными параметрами, можно использовать подпрограмму MPI_Comm_spawn_multiple. Опуская подробное описание её параметров, приведём интерфейс: int MPI_Comm_spawn_multiple(int count, char *array_of_commands[], char **array_of_argv[], int array_of_maxprocs[], MPI_Info array_of_info[], int root, MPI_Comm comm, MPI_Comm *intercomm, int array_of_errcodes[]) MPI Comm_spawn_multiple(count, array_of_commands, array_of_argv, array_of_ maxprocs, array_of_info, root, comm, intercomm, array_of_errcodes, ierror) 2008 Динамический запуск процессов Подпрограмма MPI_Get_parent возвращает интеркоммуникатор вызывающего процесса: родительский int MPI_Comm_get_parent(MPI_Comm *parent) MPI_Comm_get_parent(parent, ierr) Если данный процесс не является дочерним по отношению к какомулибо другому процессу, возвращается значение «пустого» коммуникатора MPI_COMM_NULL. 2008 Динамический запуск процессов Пример Master #include "mpi.h" int main(int argc, char *argv[]) { int world_size, universe_size, *universe_sizep, flag; MPI_Comm everyone; char worker_program[100]; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &world_size); if (world_size != 1) error("Top heavy with management"); MPI_Attr_get(MPI_COMM_WORLD, MPI_UNIVERSE_SIZE, &universe_sizep, &flag); if (!flag) { printf("This MPI does not support UNIVERSE_SIZE. How many\n\ processes total?"); scanf("%d", &universe_size); } else universe_size = *universe_sizep; if (universe_size == 1) error("No room to start workers"); choose_worker_program(worker_program); 2008 Динамический запуск процессов MPI_Comm_spawn(worker_program, MPI_ARGV_NULL, universe_size1, MPI_INFO_NULL, 0, MPI_COMM_SELF, &everyone, MPI_ERRCODES_IGNORE); MPI_Finalize(); return 0; } } 2008 Динамический запуск процессов Slave #include "mpi.h" int main(int argc, char *argv[]) { int size; MPI_Comm parent; MPI_Init(&argc, &argv); MPI_Comm_get_parent(&parent); if (parent == MPI_COMM_NULL) error("No parent!"); MPI_Comm_remote_size(parent, &size); if (size != 1) error("Something's wrong with the parent"); MPI_Finalize(); return 0; } 2008 Динамический запуск процессов Демон mpd В MPI-2 демон mpd играет важную роль. Параллельная программа может выполняться только если предварительно были запущены демоны mpd, образующие «кольцо демонов». Кольцо демонов создаётся один раз и может быть использовано многократно, разными программами одного пользователя. Кольцо демонов прекращает свой существование в результате выполнения команды mpdallexit. Кольцо демонов одного пользователя не может взаимодействовать с демонами mpd других пользователей. Благодаря демонам mpd запуск MPI-программ выполняется быстрее. Локальный экземпляр MPI-процесса завершается по нажатию клавиш Ctrl+C, при этом завершаются и все остальные процессы. Исключена возможность «зависания» процессов. Динамический запуск процессов Запуск демонов (в этом примере 5) выполняется командой: mpdboot –n 5 Проверка взаимодействия командой: демонов между собой выполняется mpdtrace Если при выполнении этой команды выводятся сообщения об ошибках, это говорит о неправильной настройке MPI или локальной сети. Завершение работы демонов выполняется командой: mpdallexit Взаимодействие между группами процессов 2008 Взаимодействие между группами процессов Взаимодействие между группами процессов MPI-2 допускает организацию обмена сообщениями между группами процессов, которые запущены независимо друг от друга. Это позволяет, например, «подключиться» к параллельной программе приложению, выполняющему обработку данных. Эта возможность полезна при создании клиент-серверных приложений и в других ситуациях. Основные механизмы взаимодействия: связь по имени; связь через порт. 2008 Взаимодействие между группами процессов Программная реализация взаимодействия: MPI_Open_port MPI_Close_port MPI_Comm_accept MPI_Publish_name MPI_Unpublish_name MPI_Lookup_name и другие подпрограммы. 2008 Односторонние обмены 2008 Односторонние обмены Односторонние обмены основаны на механизме удалённого доступа к памяти (RMA – Remote Memory Access) и позволяют процессу, инициировавшему обмен, самостоятельно задать параметры обмена как для источника, так и для адресата сообщения. Односторонние обмены используются в том случае, когда процесс «знает», какие данные другого процесса он должен модифицировать, а процесс-адресат сообщения этого не знает. Стандартная схема обмена сообщениями в этом случае требует согласования действий отправителя и получателя сообщения, для чего могут потребоваться дополнительные затраты времени (например, на пересылку параметров обмена). При этом объединены функции коммуникации и синхронизации. В односторонних обменах эти функции разделены. 2008 Односторонние обмены 2008 Односторонние обмены Односторонний обмен возможен, если процесс создаёт «окно», доступное всем остальным процессам. Окно создаётся (коллективным) вызовом подпрограммы: int MPI_Win_create(void *base, MPI_Aint size, int disp_unit, MPI_Info info, MPI_Comm comm, MPI_Win *win) MPI_Win_create(base, size, disp unit, info, comm, win, ierror) Входные параметры: base – адрес окна; size – размер окна в байтах; disp_unit – масштабный множитель для вычисления смещений; info – информационный параметр; comm – коммуникатор. Выходной параметр – win – окно. 2008 Односторонние обмены Аннулировать окно можно вызовом подпрограммы: int MPI_Win_free(MPI_Win *win) MPI_Win_free(win, ierror) 2008 Односторонние обмены Три операции одностороннего обмена: 1. MPI_Put – передача данных от отправителя в окно; 2. MPI_Get - передача данных из окна отправителю; 3. MPI_Accumulate – обновление окна получателя. Это неблокирующие операции. 2008 Односторонние обмены int MPI_Put(void *origin_addr, int origin_count, MPI_Datatype origin_datatype, int target_rank, MPI_Aint target_disp, int target_count, MPI_Datatype target_datatype, MPI_Win win) MPI_Put(origin_addr, origin_count, origin_datatype, target_rank, target_disp, target_count, target_datatype, win, ierror) Входные параметры: origin_addr – адрес буфера отправки сообщения; origin_count – количество элементов в буфере отправки; origin_datatype – тип передаваемых данных; target_rank – ранг адресата; target_disp – смещение от начала окна приёма до буфера приёма; target_count – количество принимаемых данных; target_datatype – тип принимаемых данных; win – окно приёма. 2008 Односторонние обмены При выполнении этой операции данные размещаются в буфере приёма по адресу адрес_окна + смещение × disp_unit При вызове подпрограммы MPI_Get данные копируются в обратном направлении – из памяти адресата в память «источника». 2008 Односторонние обмены Синхронизация при выполнении односторонних обменов Операции синхронизации: MPI_Win_fence MPI_Win_start, MPI_Win_complete, MPI_Win_post, MPI_Win_wait MPI_Win_lock, MPI_Win_unlock 2008 Односторонние обмены Пример subroutine example(vec1, b, map, n, comm, p) integer n, map(n), comm, p real vec1(n), b(n) integer sizeofreal, win, ierr call MPI_Type_extent(MPI_REAL, sizeofreal, ierr) call MPI_Win_create(b, n * sizeofreal, sizeofreal, & MPI_INFO_NULL, comm, win, ierr) call MPI_Win_fence(0, win, ierr) do i = 1, n j = map(i) / p k = mod(map(i), p) call MPI_Get(vec1(i), 1, MPI_REAL, j, k, 1, MPI_REAL, win, & ierr) end do call MPI_Win_fence(0, win, ierr) call MPI_Win_free(win, ierr) end 2008 Коллективные обмены 2008 Коллективные обмены В MPI-2 расширены возможности коллективных обменов сообщениями. Расширения заключаются в обобщении некоторых операций коллективного обмена на интеркоммуникаторы, введении дополнительных конструкторов интеркоммуникаторов, введении двух новых операций обмена – обобщённой операции «all-to-all» и операции исключающего сканирования. Есть и другие расширения. Подпрограмма MPI_Comm_create может использоваться для создания интеркоммуникаторов. 2008 Коллективные обмены 2008 Коллективные обмены Подпрограмма MPI_Comm_split расщепления интеркоммуникатора. может использоваться для В MPI-1 коллективные обмены ограничены интракоммуникаторами. В MPI-2 коллективные интеркоммуникаторах. 2008 обмены могут выполняться и в Коллективные обмены Пример. Операция Allgather в интеркоммуникаторе 2008 Коллективные обмены При выполнении коллективных обменов допускается использование одного буфера как для передачи, так и для приёма. Только для интракоммуникаторов! В MPI-2 имеется обобщённая операция MPI_Alltoallw. Она допускает дифференцированное задание параметров count, displacement, datatype. Смещения задаются в байтах. 2008 Внешние интерфейсы 2008 Внешние интерфейсы Механизм внешних интерфейсов позволяет программисту добавить новую функциональность поверх базовой функциональности MPI. Обобщённые запросы дают неблокирующие операции. возможность определить новые При использовании стандартных запросов операции, связанные с ними, выполняются средой исполнения MPI и приложение на этот процесс не влияет. При использовании обобщённых запросов «ответственность» за выполнение операции берёт на себя приложение. Оно сообщает MPI о завершении операции. 2008 Внешние интерфейсы Операции с обобщёнными запросами: MPI_Grequest_start MPI_Grequest_complete и некоторые другие. 2008 Другие возможности MPI-2 2008 Другие возможности MPI-2 Операции декодирования производных типов. Ассоциирование пользовательской информации с полями структуры status. Присвоение имён объектам MPI (например, коммуникаторам, окнам и др.). MPI и многопоточность (MPI_Init_thread, MPI_Thread_single, MPI_Thread_multiple и др.). Новые операции кеширования атрибутов. 2008 Другие возможности MPI-2 Параллельные операции ввода-вывода (MPI_File_open, MPI_File_close, MPI_File_read, MPI_File_write и др.). 2008 Отладка и профилирование параллельных MPI-программ 2008 Отладка и профилирование параллельных MPI-программ Отладка параллельных MPI-программ без использования специальных программных инструментов сложна и малоэффективна. Существуют разные инструменты, среди них jumpshot – собственное средство отладки MPI. Intel ® Trace Analyzer and Collector – это инструмент анализа, для которого характерно следующее: анализ выполняется на основе статистики, собранной во время выполнения программы; «инструментовка» исполняемого файла почти не влияет на производительность программы; анализ выполняется и для обменов сообщениями; поддерживается OpenMP и гибридная модель параллельного программирования MPI+OpenMP; поддерживается многопоточность Java. 2008 Отладка и профилирование параллельных MPI-программ Интерфейс программы 2008 Отладка и профилирование параллельных MPI-программ Так выглядит «плохая» программа (блокирующие обмены): 2008 Отладка и профилирование параллельных MPI-программ Так выглядит «хорошая» программа (неблокирующие обмены): 2008 Заключение В этой лекции мы рассмотрели: атрибуты; операции одностороннего обмена; внешние интерфейсы; обобщённые коллективные обмены; другие возможности MPI-2; некоторые инструменты отладки и настройки MPI-приложений. 2008 Задания для самостоятельной работы Решения следует высылать по электронной почте: parallel-g112@yandex.ru 2008 Задания для самостоятельной работы Повторите задания предыдущих лекций, используя там, где это возможно, операции одностороннего обмена, расширенную функциональность коллективных обменов MPI-2, другие возможности MPI-2. 2008