MPI за 90 минут Метод погружения Сергей Петрович Нечаев, Сибирский Суперкомпьютерный центр Параллельное программирование «Мы не можем сделать наши компьютеры быстрее прямо сейчас, но мы можем организовать одновременную работу нескольких компьютеров над одной задачей» Общая и распределенная память Средство коммуникации – общая память Коммуникации – высокоскоростная сеть Трудно построить – легко программировать Просто в постройке, трудно программировать Оценки качества параллельной программы Она делает то, что нужно Работает быстро С увеличением числа процессоров работает быстрее Ускорение и эффективность Ускорение: A=T1/Tn Эффективность: E=T1/(n*Tn) Исполнение параллельной программы Пакетный режим: 1. 2. 3. 4. 5. Поставить задачу в очередь Заняться своими делами Получить уведомление о завершении задачи Вдумчиво проанализировать результаты В случае недовольства пересмотреть программу и goto 1 Задачи системы очередей Выделение процессоров для счета Изоляция задач разных пользователей «справедливое» планирование Запуск параллельной программы Компиляция - mpicc test.c –o program На ЭВМ с системой очередей Sun Grid Engine необходимо писать специальный сценарий и ставить в очередь не команду mpirun, а этот сценарий. Пример сценария #!/bin/bash #! -V #$ -cwd mpirun -np $NSLOTS machinefile $TMPDIR/machines ./program Управление очередью Постановка задачи в очередь qsub -pe mpich N < Имя сценария > Просмотр qstat [-f] состояния очереди Удаление задачи из очереди qdel [-f] <номер задачи> Message passing interface (MPI) Инициализация и завершение среды Идентификация процессов Парные коммуникации(точка-точка) Групповые коммуникации Создание пользовательских типов данных Построение пользовательских сетей на множестве процессоров Односторонние коммуникации Порождение новых процессов Установление соединения между уже исполняющимися программами Инициализация и завершение среды #include<mpi.h> int main(int argc, char** argv) { …. MPI_Init(&argc, &argv); //Здесь параллельный код ….. MPI_Finalize(); return 0; } Идентификация Борна #include <mpi.h> 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); ….. MPI_Finalize(); return 0; } Hello, parallel world #include<mpi.h> #include<stdio.h> int main(int argc, char** argv) { int rank, size; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); printf("Hello, MPI world! I am %d of %d\n",rank,size); MPI_Finalize(); return 0; } Парные коммуникации У каждого сообщения есть Отправитель (О) и получатель (П) О должен явно вызвать функцию отправки, а П – функцию получения Есть блокирующие и не блокирующие версии Блокирующие и неблокирующие функции Неблокирующие – возвращают управление немедленно. Для того, чтобы убедиться, что передача сообщения прошла и с данными можно продолжить работу, нужны дополнительные функции. Блокирующие – Возвращение из MPI_Send не происходит до тех пор, пока данные сообщения не будут скопированы во внутренний буфер MPI. MPI_Recv возвращает управление только после того, как сообщение было принято MPI_Comm_send int MPI_Send( void *buffer,//откуда отправляем int count, //сколько MPI_Datatype type, //чего int dest, //куда int tag, //метка сообщения MPI_Comm comm //коммуникатор ) MPI_Comm_recv int MPI_Recv( void *buffer,//куда принимаем int count, //сколько MPI_Datatype type, //чего int src, // от кого int tag, //метка сообщения MPI_Comm comm, //коммуникатор MPI_Status *st //хранит полезную //информацию о том, как прошел прием ) MPI_ANY_SOURCE – от любого источника MPI_ANY_TAG – с любой меткой (только для приема) Дедлоки Неформально – ожидание события, которое никогда не произойдет Программа с дедлоком #include <mpi.h> #include <stdio.h> #include <unistd.h> int main(int argc, char** argv) { int rank,size; char hostname[100]; MPI_Status st; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); if (size!=2) { printf("This example should be run on 2 processors, now exiting\n"); MPI_Finalize(); return 0; } else { MPI_Comm_rank(MPI_COMM_WORLD,&rank); gethostname(hostname, 100); MPI_Send(hostname, 100, MPI_CHAR, !rank, 1, MPI_COMM_WORLD); MPI_Recv(hostname, 100, MPI_CHAR, !rank, 1, MPI_COMM_WORLD,&st); printf("I am %d of %d. My workmate is %s\n", rank,size,hostname); MPI_Finalize(); return 0; } } Коллективные коммуникации Топологии Коммуникатор MPI_COMM_WORLD, создаваемый при запуске MPI_Init, содержит все процессы, связанные «каждый с каждым» Есть возможность создавать свои топологии на любом подмножестве процессов из MPI_COWW_WORLD Типы Тип – карта памяти Существуют предопределенные константы для основных типов данных С. Есть возможность создавать пользовательские типы Порождение процессов В MPI 1.1 параллельная программа состоит из заранее определенного числа процессов, которое не меняется в процессе работы программы В MPI 2.0 есть средства для порождения новых процессов… Установка соединения …и установки соединения между уже исполняющимися Функция MPI_Comm_accept, которая ожидает запроса соединения от другого процесса – блокирующаяся. Неблокирующаяся версия пока отсутствует в стандарте. Односторонние коммуникации Посредством специального объекта – окна один MPI процесс может обратиться к участку памяти другого MPI процесса и стянуть/положить что-нибудь туда Источники доп. инфо http://www.mpi-forum.org unix man pages http://www.ssdonline.sscc.ru/korneev http://www.ssd.sscc.ru/nechaev Это последний слайд Здесь нужно написать что-то типа «спасибо за внимание» или «пожалуйста, вопросы», или «удачи в параллельном мире», а, может быть, «приходите к нам еще», но я так и не определился и решил временно оставить так. И за полгода текст этого слайда не изменился hpc7000.sscc.ru intelnsu $IntLab#29 qsub –pe mvapich script.sh 4 <binary> mpiicc --компиляция