27. Типы данных MPI C MPI_CHAR signed char MPI_SHORT signed short int MPI_INT signed int MPI_LONG signed long int MPI_UNSIGNED_CHAR unsigned char MPI_UNSIGNED_SHORT unsigned short int MPI_UNSIGNED unsigned int MPI_UNSIGNED_LONG unsigned long int MPI_FLOAT float MPI_DOUBLE double MPI_LONG_DOUBLE long double
47. 9 . В файле “Main. cpp” уберите комментарии для строки chunk = ContrastStretch(chunk, myrows, mycols, steps, stepby, MPI_PIXEL_T); Измените определение функций ContrastStretch в файл “app.h” : PIXEL_T **ContrastStretch(PIXEL_T **image, int rows, int cols, int steps, int stepby, MPI_Datatype MPI_PIXEL_T ); Откройте файл ContrastStretch.cpp и измените код в соответствии с PIXEL_T **ContrastStretch(PIXEL_T **image, int rows, int cols, int steps, int stepby, MPI_Datatype MPI_PIXEL_T ) { cout << myRank << " (" << host << "): Processing " << rows << "rows," << cols << " cols..." << endl; PIXEL_T **image2 = New2dMatrix<PIXEL_T>( rows+2 , cols); MPI_Status status; int tag = 0; // Обратите внимание на два дополнительных строки Выполнение упражнения
MPI – стандарт системы параллельного программирования, основанный на модели обмена сообщениями. Версия стандарта 1.1 описывает следующие виды взаимодействия параллельных процессов : Парные обмены – группа операций приема-передачи сообщений от одного процесса другому. Коллективные операции – к ним относятся операции, задействующие все или ряд выделенных процессов. Операции над группами и коммуникационными контекстами – операции, связанные с порождением и управлением группами процессов и коммуникаторами Топологии процессов – в этом разделе стандарта описаны поддерживаемые MPI виды процессных топологий и операции работы с ними Привязки к языкам – пожалуй, самый большой раздел стандарта, описывающий синтаксис вызовов библиотечных функций MPI из программ на C и Fortran . Основными реализациями являются MPICH и MSMPI
Необходимо понимать, что MPI описывает семантику взаимодействия процессов только через СВОИ функции. Если программист пользуется другими инструментами – прямой доступ к памяти и сети, межпроцессные обмены и каналы – он делает это на свой страх и риск Также, одной из основных целей создания MPI как стандарта было разработать кросс-платформенное решение. Ни одна операция MPI не является платформозависимой, лишь конкретная реализация стандарта будет вносить свои коррективы прозрачно для программиста. В стандарт не входят описания инструментов отладки и поддержки управления задачами, хотя бы потому, что такие инструменты являются платформозависимыми. Поставщики реализаций MPI могут поставлять такие инструменты самостоятельно.
Локальной называется операция, не вызывающая взаимодействия процессов. Используя такую операцию мы можем быть уверены, что никакой обмен информацией между процессами не производится. Нелокальная операция МОЖЕТ при необходимости вызывать взаимодействие процессов. Необходимо отметить, что одна и та же операция в зависимости от условий ее выполнения может быть как локальной, так и нелокальной – см. пример MPI_Send с буферизацией и без Коллективная операция – это операция, которая ОБЯЗАТЕЛЬНО должна вызываться всеми процессами группы, в рамках которой она производится. Если некоторый процесс группы опрацию не вызывает, это считается ошибкой хода выполнения.
Блокирующие функции подразумевают выход из них только после полного окончания операции, т.е. вызывающий процесс блокируется, пока операция не будет завершена. Для функции посылки сообщения это означает, что все пересылаемые данные помещены в буфер (для разных реализаций MPI это может быть либо какой-то промежуточный системный буфер, либо непосредственно буфер получателя). Для функции приема сообщения блокируется выполнение других операций, пока все данные из буфера не будут помещены в адресное пространство принимающего процесса. Ресурсы, указанные в ее параметрах, можно безопасно использовать повторно (например, данные переданы или скопированы в системный буфер). Неблокирующие функции подразумевают совмещение операций обмена с другими операциями, поэтому неблокирующие функции передачи и приема по сути дела являются функциями инициализации соответствующих операций. Для опроса завершенности операции (и завершения) вводятся дополнительные функции.
Префикс S (synchronous) - означает синхронный режим передачи данных. Операция передачи данных заканчивается только тогда, когда заканчивается прием данных. Функция нелокальная. Префикс B (buffered) - означает буферизованный режим передачи данных. В адресном пространстве передающего процесса с помощью специальной функции создается буфер обмена, который используется в операциях обмена. Операция посылки заканчивается, когда данные помещены в этот буфер. Функция имеет локальный характер. Префикс R (ready) - согласованный или подготовленный режим передачи данных. Операция передачи данных начинается только тогда, когда принимающий процессор выставил признак готовности приема данных, инициировав операцию приема. Функция нелокальная. Префикс I (immediate) - относится к неблокирующим операциям. Все функции передачи и приема сообщений могут использоваться в любой комбинации друг с другом. Функции передачи, находящиеся в одном столбце, имеют совершенно одинаковый синтаксис и отличаются только внутренней реализацией. Поэтому в дальнейшем будем рассматривать только стандартный режим, который в обязательном порядке поддерживают все реализации MPI.
С момента своего запуска любая правильно написанная MPI программа должна пройти следущие этапы : Запуск – программа запускается планировщиком. Инициализация среды – на этом этапе в среде MPi устанавливаются необходимые параметры и порождаются внутренние объекты. Также именно здесь создается глобальный и локальный коммуникаторы MPI_COMM_WORLD и MPI_COMM_SELF Непосредственно вычисления – выполнение кода, написанного программистом. Завершение среды MPI – освобождение ресурсов, занятых средой MPI . Повторная инициализация среды невозможна. Выгрузка – освобождение ресурсов, занятых процессами, их закрытие.
Любая MPI-программа (приложение) должна начинаться с вызова функции инициализации MPI: функции MPI_Init. В результате выполнения этой функции создается группа процессов, в которую помещаются все процессы приложения, и создается область связи, описываемая предопределенным всеобщим коммуникатором MPI_COMM_WORLD. Эта область связи объединяет все процессы-приложения. Процессы в группе упорядочены и пронумерованы от 0 до groupsize-1, где groupsize равно числу процессов в группе. Кроме этого, создается предопределенный коммуникатор MPI_COMM_SELF, описывающий свою область связи для каждого отдельного процесса. До вызова MPI_INIT любые вызовы функций MPI, кроме MPI_Initialized , будут заканчиваться ошибкой. В программах на C каждому процессу при инициализации передаются аргументы функции main, полученные из командной строки. В программах на языке FORTRAN параметр IERROR является выходным и возвращает код ошибки.
Если какой-либо процесс закончит свою работу до вызова MPI_Finalize (например, аварийно) , MPI- программа завершится с ошибкой. После вызова MPI_Finalize невозможен вызов ни одной функции библиотеки MPI
Рассмотрим структуру MPI приложения: mpi.h – заголовочный файл, в котором определены все функции MPI argv – аргументы командной строки argc – их количество Пример - simplest
После выполнения этой функции в переменную flag будет сохранено значение 1 ( True) , если среда выполнения MPI инициализирована (то есть, был успешный вызов MPI_Init()) , либо 0, в противном случае.
MPI _Comm_size возвращает в переменную size количество процессов в коммуникаторе comm. Подставляя в эту функцию всеобщий коммуникатор MPI_COMM_WORLD мы можем получить количество процессов, на которых запущена программа.
MPI_Comm_rank возвращает номер процесса (то есть, ранг), вызвавшего ее. Номера процессов всегда лежат на интервале 0 … < размер группы > – 1 и, естетсвенно, уникальны для каждой группы. При этом ранг процесса в коммуникаторе MPI_COMM_WORLD можно считать абсолютным рангом процесса. Ранги процесса в других (созданных программистом, об этом в MPI advanced techniques) будут относительными.
Сообщения в MPI передаются как пакеты, содержащие данные вместе с информацией их описывающей. Наличие информации, например, о реальном размере передаваемых данных может оказаться весьма полезной в некоторых случаях. При отправке сообщения мы должны указать следующие параметры : Собственно, массив данных, которые мы хотим передать. Размер массива передаваемых данных – система выполнения должна точно знать, сколько данных мы передаем. О типах данных ниже. Процесс-получатель сообщения – при парных обменах вполне естественный параметр. Тег данных – это специальная метка, позволяющая в программе отличать одни данные от других. Тег задается программистом произвольно. При приеме сообщения также необходимо указывать буфер данных, в котором данные принятого сообщения будут сохранены, размер этого буфера, тег принимаемых данных и процесс-отправитель сообщения. При вызове команды получения данных система будет искать сообщение с запрошенными тегом и процессом-отправителем. Если будет найдено сообщение с указанными значениями, оно будет принято и сохранено. Иначе система будет ждать, пока нужное сообщение появится.
Сообщения в MPI передаются как пакеты, содержащие данные вместе с информацией их описывающей. Наличие информации, например, о реальном размере передаваемых данных может оказаться весьма полезной в некоторых случаях. При отправке сообщения мы должны указать следующие параметры : Собственно, массив данных, которые мы хотим передать. Размер массива передаваемых данных – система выполнения должна точно знать, сколько данных мы передаем. О типах данных ниже. Процесс-получатель сообщения – при парных обменах вполне естественный параметр. Тег данных – это специальная метка, позволяющая в программе отличать одни данные от других. Тег задается программистом произвольно. При приеме сообщения также необходимо указывать буфер данных, в котором данные принятого сообщения будут сохранены, размер этого буфера, тег принимаемых данных и процесс-отправитель сообщения. При вызове команды получения данных система будет искать сообщение с запрошенными тегом и процессом-отправителем. Если будет найдено сообщение с указанными значениями, оно будет принято и сохранено. Иначе система будет ждать, пока нужное сообщение появится.
Функция MPI_Send производит отпарвку сообщения из буфера buf процессу с рангом dest в коммуникаторе comm . Далее будут рассмотрены различные режимы работы MPI_Send . Необходимо отметить, что по усмотрению системы выполнения MPI , функция MPI_Send может сохранять передаваемые данные в буфер и возвращать управление в программу, а может блокировать ее выполнение до появления запроса на прием сообщения, посылаемого функцией MPI_Recv .
MPI_Recv выполняет прием сообщения от процесса source . Выполнение программы будет заблокировано до того момента, как собщение будет полностью сохранено в буфере buf . В параметре status будет сохранен статус сообщения : его тег, его полный размер (он может быть и больше заданного параметром MPI_Recv ) и процесс-отправитель.
В таблице приведены типы данных MPI для языков C и Fortran. MPI содержит специальные функции, позволяющие конструировать собственные типы данных – об этом в MPI Advanced techniques .
MS-MPI входит в состав HPC Pack 2008 . Также скачать SDK можно со страницы http://go.microsoft.com/fwlink/?linkID= 127031 Допустим, что SDK установлен в папку is C:Program FilesMicrosoft HPC Pack 2008 SDK. При выборе платформы Win32 необходимо указать директории для подключаемых и библиотечных файлов. Для этого необходимо в Visual Studio выбрать в меню Обратите внимание, что эти действия необходимо проделать для каждого типа проекта: Win32 Debug, Win32 Release, x64 Debug, и x64 Release.
Изменим алгоритм изменения контрастности изображения для работы на кластере. Необходимо чтобы MPI приложение выполняло следующий алгоритм: 1. Распределение частей матрицы изображения равномерно между вычислительными узлами 2. Каждый узел выполняет преобразования над полученной частью матрицы 3. Результирующую матрица формировалась из данных полученных от каждого узла. Для начала создадим приложение в котором главный узел рассылает по вычислительным узлам части матрицы, а потом они Посылают эти части матирицы на главный узел, который собирает их в одну матрицу. Проверку исходной матрицы и полученной будет производить с помощью программы WinDiff
Для всех узлов кроме первого вычисление контрастности пикселей начинается со первой строки матрицы и заканчивается предпоследней ( rows-1 ) строкой матрицы изображения. Первый узел начинает вычисления со 2-й строки т.к. для него нет 0-й (пересылаемой). Тоже самое можно сказать и о последнем узле, вычисления которого останавливаются на предпоследней строке.
Как вы помните, для реализации алгоритма изменения контрастности, для всех узлов кроме первого и последнего требуются дополнительные строки.
Обратите внимание на окончание цикла for , где отображается количество измененных пикселей, и если нет измененных пикселей, вычисления останавливаются: } } cout << &quot; (diffs until convergence: &quot; << diffs << &quot;)&quot; << endl; converged = (diffs == 0); В последовательной версии алгоритма переменная diffs объявлена как локальная. В связи с тем, что вычисление контрастности происходит на множестве узлов, переменная diffs должна быть объявлена как глобальная , таким образом чтобы узлы могли передавать значения локальной переменной diffs главному узлу. Главный узел вычисляет сумму, и если она равна 0, то вычисления прекращаются, в ином случае отправляет новое значение вычислительным узлам